Worker 生命周期与消息协议
Worker 把计算移出主线程,但它不会共享页面的 DOM 或普通 JavaScript 堆。学习重点是建立消息协议、处理错误,并在页面离开时终止资源。
最小实验:可运行的模块 Worker
在同一目录创建 index.html、main.js、worker.js,用 npx serve . 或其他静态服务器打开 index.html(不能直接使用 file://)。
worker.js:
js
self.addEventListener('message', ({ data }) => {
if (data.type !== 'square') return
const result = data.value * data.value
self.postMessage({ type: 'result', requestId: data.requestId, result })
})main.js:
js
const worker = new Worker('./worker.js', { type: 'module' })
const requestId = crypto.randomUUID()
worker.addEventListener('message', ({ data }) => {
if (data.requestId === requestId) console.log('result', data.result)
})
worker.addEventListener('error', (event) => console.error('worker error', event.message))
worker.postMessage({ type: 'square', requestId, value: 7 })
window.addEventListener('pagehide', () => worker.terminate(), { once: true })预期控制台先输出 result 49。把 value 改成字符串,或把 worker.js 中的 result 改成不存在的变量,分别观察协议数据错误和 error 事件。刷新页面后,旧 Worker 不应继续输出日志。
机制与边界
postMessage 传递的是结构化克隆后的值;不能传递 DOM 节点,也不能直接读取页面变量。每条消息应有类型和请求 ID,避免多个请求返回时无法匹配。terminate() 是立即终止;需要可协作取消时,应额外设计 cancel 消息并让 Worker 定期检查。
练习
- 为协议增加
cancel消息,让一个循环计算在收到取消后返回{ type: 'cancelled' }。 - 故意让 Worker 抛错,说明页面端如何区分协议错误和脚本加载错误。
- 删除
pagehide清理并通过刷新观察旧资源,写出为什么单页应用切换路由时也需要显式终止。
下一步
继续学习 structured clone 与 Transferable,再回到本节比较复制和转移的所有权变化。