javascript Worker怎么用_如何实现多线程编程来避免界面阻塞?

Worker 是浏览器提供的多线程机制,用于在后台线程执行耗时任务以避免阻塞主线程;需独立 JS 文件,通过 postMessage/onmessage 通信,数据经序列化复制传递。

JavaScript 的 Worker 是浏览器提供的多线程机制,它让耗时任务(如大量计算、数据处理、加密解密)在后台线程中运行,不占用主线程,从而避免阻塞页面渲染和用户交互。

Worker 的基本用法

Worker 需要将逻辑写在独立的 JS 文件中,通过 new Worker() 实例化并通信。主线程和 Worker 之间不能共享变量或 DOM,只能靠 postMessage()onmessage 传递数据(序列化后复制,不是引用)。

  • 创建一个文件 worker.js,内容为:
    self.onmessage = function(e) {
      const data = e.data;
      // 模拟耗时计算
      let result = 0;
      for (let i = 0; i < data * 1000000; i++) {
        result += i;
      }
      self.postMessage({ result });
    };
  • 在主页面中使用:
    const worker = new Worker('worker.js');
    

    worker.onmessage = function(e) { console.log('计算结果:', e.data.result); };

    worker.postMessage(100); // 向 worker 发送数据

  • 注意:Worker 无法访问 window、document、alert 等主线程 API,也不能直接操作 DOM;若需更新界面,必须由主线程接收消息后再操作。

如何传复杂数据(如大数组、对象)?

Worker 支持结构化克隆(structured clone),可传普通对象、数组、Map、Set、Date、RegExp 等(但不能传函数、DOM 节点、undefined)。对超大数组,建议启用 Transferable Objects 提升性能——把 ArrayBuffer 或 TypedArray 的所有权转移过去,避免拷贝:

  • 主线程发送时:
    const buffer = new ArrayBuffer(1024 * 1024);
    const array = new Uint8Array(buffer);
    // 填充数据...
    worker.postMessage({ data: array }, [buffer]); // 第二个参数表示转移
  • Worker 中接收后即可直接使用该 buffer,主线程中该 buffer 自动变为 null。

常见避坑点

  • 路径问题:Worker 构造函数中的路径是相对于 HTML 页面的,不是相对于当前 JS 文件(ESM Worker 除外)。
  • 同源限制:Worker 脚本必须与主页面同源,否则会报错;本地 file:// 协议下多数浏览器禁用 Worker。
  • 错误捕获:Worker 内部报错不会冒泡到主线程,需监听 worker.onerror 或在 worker 内用 self.onerror 处理
  • 及时终止:不再需要时调用 worker.terminate(),否则线程会持续运行,浪费资源。

进阶:使用模块化 Worker(ESM Worker)

现代浏览器支持以模块方式加载 Worker(需服务端支持 CORS):

  • 创建 worker.mjs,用 import 引入其他模块;
  • 主线程中:
    const worker = new Worker('worker.mjs', { type: 'module' });
  • 模块 Worker 支持 import.meta.url、动态 import 等特性,更利于组织复杂逻辑。