普通视图

发现新文章,点击刷新页面。
昨天 — 2025年8月17日首页

也是用上webworker了

作者 小遁哥
2025年8月17日 14:08

React 16.8,我自己写的的足球应用,问题是模态框没有立即弹出,反而是等了一会才弹出。

  useImperativeHandle(ref, () => ({
    showModal: (id: string) => {
      const newState = {
        ...state,
        id,
        teamCount: teamOddList.length,
        tableLoading: true,
        open: true,
      };
      setState(newState);
      setAddedItems(new Map());

      // 初始化已存在的奖金项目
      initializeAddedItems(id);

      getOddResultList(newState, teamOddList);
    },
  }));

我开始以为是 React 响应式设计导致的,因为此时 setState 的是异步的,虽然 showModal 中设置了 open 为 true,后续处理不当,还是会导致 open 隐式设置为 false

仔细检查一番发现没有,并且我已经把 newSate 传递过去了,通过注释代码发现,是getOddResultList导致的,其实在之前的写法中我是加了setTimeout的,只不过时间太久了忘记了为什么加。

如今再写一来觉得setTimeout这种解决方式并不好。而且延迟 20ms 是没用的,看起来像是在等计算完成后再显示模态框。

于是我问了下 Trace,他说是因为getOddResultList有大量的同步计算,建议我用 webworker 来处理。

  • 多重嵌套循环 :对每个球队的赔率信息进行多维度组合计算(胜平负、让球、比分、进球数、半场等)
  • 指数级复杂度 :通过递归函数 getTeamCombinationList 和 againForEach 生成所有可能的投注组合
  • 大量数据处理 :每个组合都需要计算赔率乘积,并进行排序
  • JavaScript 是单线程的,同步计算会完全阻塞主线程
  • 模态框的 open 状态虽然已设置为 true ,但 React 无法进行重新渲染
  • 用户界面会出现"卡顿",模态框无法立即显示

这个函数确实会消耗大量的时间,因为有 4 只球队会产生 9 百万种结果。

使用 webworker 的话,需要将 js 文件放到 public 目录下,通过onmessage来接收消息,通过postMessage来发送消息。

self.onmessage = function (e) {
  const { teamOddList } = e.data;
  try {
    const result = calculateOddResultList(teamOddList);
    self.postMessage({ success: true, data: result });
  } catch (error) {
    self.postMessage({ success: false, error: error.message });
  }
};

在 jsx 文件中使用 webworker

useEffect(() => {
    // 创建 Web Worker

    workerRef.current = new Worker('/oddResultWorker.js');
    workerRef.current.onmessageerror = (e) => {
      console.error('Worker message error:', e);
    };

    // 监听 Worker 消息
    workerRef.current.onmessage = (e) => {
      const { success, data, error } = e.data;
      if (success) {
        allOddResultListRef.current = data;
        setState((preState) => ({
          ...preState,
          total: data.length,
          tableLoading: false,
          oddResultList: data.slice(
            (preState.currentPage - 1) * pageSize,
            preState.currentPage * pageSize
          ),
        }));
      } else {
        console.error('Worker error:', error);
      }
    };

    return () => {
      if (workerRef.current) {
        workerRef.current.terminate();
      }
    };
  }, []);

调用时

    showModal: (id: string, teamOddList: Array<NFootball.ITeamRecordOdds>) => {
      ...
      setAddedItems(new Map());
      initializeAddedItems(id);

      workerRef.current.postMessage({ teamOddList });
    },
❌
❌