把计算与页面响应分开
Web Worker 是浏览器提供的后台脚本执行机制。页面用 Worker 构造器指定脚本,浏览器为它建立独立的全局环境与事件循环;它不是在页面函数旁简单增加一个异步回调。将耗时计算放到这样的环境中,可以减少它对页面主线程处理交互的阻塞。
例如页面需要对一批图像像素做计算,可把数据交给 Worker,继续响应用户操作,待结果回来后再更新显示。Worker 不能直接读取或修改所属页面的 DOM,也不能直接使用那个页面的对象。需要改动界面时,应把结果发回主线程,由页面代码完成相应更新。独立执行上下文并不意味着所有浏览器 API 都在其中可用。
消息、克隆与所有权转移
页面与 Worker 通常通过 postMessage 发送消息,接收方在 message 事件中取得数据。这种通信把两边的计算边界显式化:发送消息不是同步调用对方函数,也不意味着发送者可以立即取得对方的返回值。应用需要自行约定消息种类、请求标识与结果处理方式。
普通结构化数据经过结构化克隆传递,接收方得到相应的数据副本,而非自动与发送方共享同一个对象。结构化克隆的范围也不同于把内容转成 JSON 字符串,例如可以处理循环引用。因此,不能把消息机制概括为任意对象都能原样跨线程使用。
对于 ArrayBuffer 等可转移对象,可以把它列入转移列表,将其底层资源交给另一侧,避免对大块数据进行通常的复制。转移后,发送方原来的缓冲区会被分离,不能继续按原方式使用。减少复制成本与保留两边同时访问的能力不是同一件事。
专用与共享的连接方式
专用 Worker 由创建它的一方使用,双方通过 Worker 对象所代表的通信通道交换消息。共享 Worker 使用 SharedWorker 接口,可以让符合条件的多个页面或其他上下文接入同一个后台工作环境,并通过各自的 MessagePort 通信。它适合集中处理需要由多个连接共同使用的状态。
共享不是任意网站之间都能连接:相关上下文须满足同源要求,规范还会根据存储键等条件确定能否复用同一实例。不同窗口也不必然共用一个 Worker。这里所说的专用与共享工作线程,不能把名称简写理解为 Service Worker;后者有不同的注册和生命周期模型。
共享内存与生命周期的限制
SharedArrayBuffer 是另一路径,它允许不同执行上下文访问同一段内存,而不是转移后只由一侧持有。使用这一能力有额外安全限制:页面需要满足跨源隔离条件,通常涉及 COOP 与 COEP 响应头,相关权限策略也不能禁止该能力。程序还须处理并发访问的同步问题,不能把共享内存看作自动安全的普通对象传参。
Worker 有启动、内存和通信成本。小任务若反复创建 Worker,或不断复制大量数据,整体耗时未必下降;改善界面响应也不等于计算本身更快。页面可以终止专用 Worker,Worker 也可以关闭自己,其存活受关联上下文和浏览器生命周期规则约束。即使某些共享 Worker 可以短暂延长存活,也不能把它当作永不停止的后台服务。