前言
在上一篇 << 单线程 与 执行机制 >> 中, 我们提到了 Web Worker.
它的诞生是为了解决 JS 主线程执行耗时计算时, 导致 UI 无法及时更新的卡死现象.
它的解决思路是把同步代码异步化. 原本需要 JS 主线程执行的运算, 转交给另一条线程去完成.
这和 setTimeout 是同一个原理. 计数也不是 JS 主线程做的, 而是定时器触发线程做的.
参考
Worker 基本使用
The Problem
假设我们有一个复杂运算 for loop 50亿次 (耗时 5 秒)
document.querySelector('h1').textContent = 'Hello World'; for (let i = 0; i < 5_000_000_000; i++) {} // 需要 5 秒钟 console.log('do something else');
如果我们让 JS 主线程来处理, 那么 UI 渲染就会慢 5 秒钟, 用户迟迟才会看见 Hello World 出现.
New Web Worker
这时我们可以开启 Web Worker, 它会创建另一条线程去处理这个 for loop. 然后我们把后续的代码变成一个 callback.
worker.js
for (let i = 0; i < 5_000_000_000; i++) {} // 需要 5 秒钟 self.postMessage('done'); // 通知主线程, 任务完成 self.close(); // 可以在子线程关闭, 也可以交由主线程关闭.
index.js
document.querySelector('h1').textContent = 'Hello World'; const worker = new Worker('./worker.js', { name: 'worker' }); // callback worker.addEventListener('message', () => { console.log('do something else'); worker.terminate(); // 关闭子线程 });
通过 addEventListener 注册 callback, 这样原本同步的代码就变成异步了.
Worker 沟通
主线程和子线程 (worker) 的沟通是通过 postMessage 和 addEventListener('message') 完成的.
postMessage 类似于 dispatchEvent 的意思.
主线程和子线程都可以 postMessage 和 listen message, 也都可以关闭子线程.
主线程也能监听子线程的错误
// index.ts – main thread worker.postMessage('message'); // dispatch event to child thread worker.addEventListener('message', () => {}); // listen event from child thread worker.addEventListener('error', () => {}); // listen error from child thread worker.terminate(); // close child thread // worker.ts – child thread self.postMessage('done'); // dispatch event to main thread self.addEventListener('message', () => {}); // listen event from main thread self.close(); // close child thread (把自己关了)
Message Data Type
// index.ts const worker = new Worker('./worker.js', { name: 'worker' }); const file = document.querySelector('input').files[0]; file.arrayBuffer().then(arrayBuffer => { worker.postMessage({ value: 'value', buffer: arrayBuffer }, { transfer: [arrayBuffer] }); }); // worker.ts self.addEventListener('message', event => { const { value, buffer } = event.data; });
两个点要注意
1. postMessage 可以传对象, 但是对象会被 deep copy.
2. ArrayBuffer 也会被 copy, 有时候 size 可能会太大, 所以 postMessage 有个设置叫 transferable objects
声明 transfer 以后, ArrayBuffer 就转交给 worker 了 (no more copy), 这时主线程就不可以读取 ArrayBuffer了.
Import Script in Worker
self.importScripts('/imported-script.js');
worker 内可以 import 其它的 script.
imported-script.js 也可以调用 self.postMessage 和主线程沟通.
而 worker 和 imported-script 要沟通则是用全局变量 e.g. self.value = 'some value'.
SharedWorker
目前支持率不是很好, 以后才研究 TODO...
标签:postMessage,Web,thread,Worker,self,JavaScript,worker,线程 From: https://www.cnblogs.com/keatkeat/p/16840670.html