本文已参与「新人创作礼」活动,一起开启掘金创作之路。
什么是 web worker?
JavaScript 是一种单线程的语言,Web worker 的作用就是为 JavaScript 创造多线程环境。允许主线程创建 Workder 线程,将一些任务分配给后者运行,主线程运行的同时,worker 线程也在运行,相互不干扰,等 worker 线程运行完后,把结果返回给主线程。
使用
主线程发送和接受数据
// 主线程 index.js
// 创建一个 worker
const worker = new Worker('./worker.js');
// 发送消息
worker.postMessage('Hello World');
// 接受数据
worker.onmessage = function(e) {
console.log(e.data); // 这里可以对接受到的数据进行一些处理
console.log('Message received from worker');
}
// 终止
worker worker.terminate();
// 错误处理
worker.onerror = function(e) => { }
worker 线程对数据进行二次加工,一般处理一些比较耗时的任务(比如代码高亮)
// worker 线程 worker.js
onmessage = function (e) {
// 一些复杂的计算或者耗时的事情
const msg = `post-msg-${e.data}`;
postMessage(msg);
// 在 worker 线程内部可以是 close() 来关闭
}
ps:
- 在主线程中,使用 onmessage 和 posMessage() 必须挂在 worker 对象上。
- 而在 worker 线程中不需要这么做,因为在 worker 线程内部,worker 是有效的全局作用域
数据传递
主线程与 worker 线程的数据传递是值拷贝的关系,即是传值而不是地址。意味着在 worker 中修改通信的数据,主线程中的内容不会被修改。
局限性
一个 worker 运行一个 JavaScript 文件,这个文件即在 worker 线程上运行的代码。
worker 运行在一个不同于 window 的全局上下文中,因此在 worker 内通过 window 获取全局作用于将会返回错误。
- 不能直接操作 DOM 节点
- 不能使用 window 对象的默认方式和属性,不过可以使用 WebSockets、IndexedDB 等数据存储机制,具体可以查看 developer.mozilla.org/en-US/docs/…。
在 React 中使用 Web Workers
web worker 需要指定一个脚本的 URI,而在 React 的实际项目中,我们一般都会和打包工具一起使用(比如:webpack),这时如果直接引用文件,就会发生错误
// index.tsx
const worker = new Worker('./worker.ts')
解决方式
如果使用 webpack ,可以通过 worker-loader 来解决,配置如下:
{
module: {
rules: [
{
test: /\.worker\.ts$/,
use: [
{
loader: 'worker-loader',
options: {
inline: 'fallback',
},
},
{
loader: 'ts-loader',
},
],
}
]
}
}
worker-loader 会把文件加载为 web worker,我们只需要把文件名命名为 /\.worker\.ts$/ 格式,比如 msg.worker.ts
可能会遇到问题
- 在使用了web worker 的页面,多次操作后,页面出现卡顿或者直接卡死
可能原因:
检查多次操作后,页面是否存在多个 web worker。修改创建 web worker 的逻辑,使页面不要同时出现 n 个 web worker