如何运用 Atomics.waitAsync 在不阻塞主线程条件下跨 Worker 等待共享状态的原子同步

作者:袖梨 2026-06-26
Atomics.waitAsync 仅限 Worker 使用,需 SharedArrayBuffer 和跨源隔离(COOP/COEP),否则抛 TypeError;等待前须用 Atomics.load 确认值,通知须匹配位置与数量,并加超时保护。

Atomics.waitAsync 不能在主线程直接使用,必须配合 SharedArrayBuffer 和跨源隔离环境,否则会抛出 TypeError: Atomics.waitAsync is not supported

为什么主线程调用 Atomics.waitAsync 会失败

浏览器出于安全考虑,默认禁用 SharedArrayBuffer,而 Atomics.waitAsync 依赖它。即使你手动创建了 SharedArrayBuffer,只要页面未启用跨源隔离(COOP/COEP),Atomics.waitAsync 就不可用,且不会报错“不支持”,而是直接抛出类型错误。

  • 检查是否启用:运行 typeof SharedArrayBuffer !== 'undefined'Atomics.waitAsync 是否为函数
  • 关键前置条件:服务器响应头必须包含 Cross-Origin-Opener-Policy: same-originCross-Origin-Embedder-Policy: require-corp
  • 本地开发时,不能用 file:// 协议,需启动本地 HTTP 服务(如 python3 -m http.server 8000 --bind 127.0.0.1

Worker 中正确初始化共享内存与原子等待

主线程负责分配共享内存并传给 Worker;Worker 用 Int32Array 视图绑定该内存,并用 Atomics.waitAsync 监听变化。注意:等待前必须先用 Atomics.load 确认当前值,否则可能错过初始状态。

  • 主线程创建缓冲区:const sab = new SharedArrayBuffer(4); const iv = new Int32Array(sab); iv[0] = 0;
  • Worker 构造时传入:const worker = new Worker('worker.js'); worker.postMessage({ sab });
  • Worker 内接收并建立视图:const iv = new Int32Array(e.data.sab);
  • 等待逻辑必须写在 async 函数中:const { value, asyncId } = await Atomics.waitAsync(iv, 0, 0);(等待位置 0 的值从 0 变更为其他值)

如何避免 waitAsync 永远 pending 或误触发

Atomics.waitAsync 不是轮询,它依赖底层线程调度器的唤醒机制。如果通知方没用 Atomics.notify,或者 notify 的位置/数量不匹配,等待就会一直挂起。更隐蔽的问题是:notify 必须发生在 waitAsync 被调用之后、且同一共享内存视图上。

  • 通知方必须用 Atomics.notify(iv, 0, 1) —— 第三个参数是唤醒等待者的数量,1 表示只唤醒一个(哪怕有多个 await)
  • 不能混用不同视图(比如主线程用 Uint32Array,Worker 用 Int32Array),字节偏移和类型必须一致
  • 等待前建议加超时保护:Promise.race([Atomics.waitAsync(iv, 0, 0), new Promise((_, r) => setTimeout(r, 5000))])
  • waitAsync 返回的 value 是当前内存值(非通知值),asyncId 是内部标识,仅用于调试,不可用于业务判断

实际跨 Worker 同步场景中的典型结构

比如主线程启动一个计算型 Worker,等它完成再渲染结果。这时不要用 postMessage 回传数据再处理,而是让主线程通过 Atomics.waitAsync 等待 Worker 写入完成标记 —— 这样能省掉一次序列化/反序列化,也避免消息队列积压。

  • Worker 完成后执行:Atomics.store(iv, 0, 1); Atomics.notify(iv, 0, 1);
  • 主线程中不能直接 await waitAsync(主线程禁止),所以必须把等待逻辑放到另一个 Worker 里,或改用 Atomics.wait + setTimeout 模拟(不推荐)
  • 真正可行的模式是:主线程发任务 → Worker A 执行 → Worker A 通知 Worker B → Worker B 调用 Atomics.waitAsync 并触发后续逻辑 → Worker B 用 postMessage 通知主线程
  • 也就是说,Atomics.waitAsync 的使用者只能是 Worker,且不能是发起同步请求的那个 Worker —— 它天然适合构建“监听型中间层”

最易被忽略的一点:Atomics.waitAsync 的等待行为受浏览器线程调度策略影响,某些低功耗设备或后台标签页中可能延迟数秒才响应 notify。生产环境务必搭配超时和 fallback 机制,不能假设它总能“即时”唤醒。

相关文章

精彩推荐