Web Worker 可以使脚本运行在新的线程中,它们独立于主线程,可以进行大量的计算活动,而不会影响主线程的 UI 渲染。当计算结束之后,它们可以把结果发送给主线程,从而形成了高效、良好的用户体验。Web Worker 是一个统称,具体可以细分为普通的 Worker、SharedWorker 和 ServiceWorker 等。
在 worker 线程中可以运行任何代码,不过有一些例外情况。因为 workers 运行在另一个全局上下文中,所以
workers 和主线程间的数据传递
postMessage()
:发送各自的消息onmessage
:响应消息(event.data
)这个过程中数据并不是被共享而是被复制。
const worker = new Worker('./worker.js'); // 参数是url,这个url必须与创建者同源
2.Worker 的方法
onmessage 主线程中可以在 Worker 上添加 onmessage 方法,用于监听 Worker 的信息。
示例:
const worker = new Worker('./worker.js');
worker.onmessage = function (messageEvent) {
console.log(messageEvent)
}
onmessageerror 主线程中可以在 Worker 上添加 onmessageerror 方法,用于监听 Worker 的错误信息。
示例:
const worker = new Worker('./worker.js');
worker.onmessageerror = function (messageEvent) {
console.log(messageEvent)
}
postMessage() 主线程通过此方法给 Worker 发送消息,发送参数的格式不限(可以是数组、对象、字符串等),可以根据自己的业务选择。
示例:
const worker = new Worker('./worker.js');
worker.postMessage({ type: 'start', payload: { count: 666 } }); // 发送信息给worker
terminate() 主线程通过此方法终止 Worker 的运行。
示例:
const worker = new Worker('./worker.js');
worker.terminate();
3.通信
Worker 的作用域跟主线程中的 Window 是相互独立的,并且 Worker 中是获取不到 DOM 元素的。所以在 Worker 中你无法使用 Window 变量。取而代之的是可以用 self 来表示全局对象。self 上有哪些方法和属性,感兴趣的小伙伴可以自行输出查看。比较常用的方法是 onmessage、postMessage,主要用来跟主线程进行通信。
示例:
// 监听事件,主线程可以通过 postMessage 发送信息过来
self.onmessage = (messageEvent) => {
const { type, payload } = messageEvent.data;
switch (type) {
case 'start':
// 通过 type 去区分不同的业务逻辑,payload 是传过来的数据
const result = 0;
// ....,通过一系列处理之后,把最终的结果发送给主线程
this.postMessage(result);
break;
}
};
这里我们从 messageEvent.data 中获取从主线程传递过来的数据。为了业务的扩展性,这边是以 type 去区分不同的业务,payload 承载数据源,通过处理之后把结果发给主线程。主线程的 onmessage 回调函数中就能收到这个结果了。
SharedWorker 是一种特定的 Worker。从它的命名就能知道,它是一种共享数据的 Worker。它可以同时被多个浏览器环境访问。这些浏览器环境可以是多个 window, iframes 或者甚至是多个 Worker,只要这些 Workers 处于同一主域。为跨浏览器 tab 共享数据提供了一种解决方案。
var myWorker = new SharedWorker("worker.js");
与专用 Worker 一个最大的区别是:共享 worker 通信必须通过端口对象(一个确切的打开的端口),在专用 worker 中这一部分是隐式进行的。
在传递消息之前,端口连接必须被显式的打开,打开方式是使用 onmessage
事件处理函数或者 start()
方法。如果消息事件被 addEventListener()
方法使用,则选择 start()
打开端口:
myWorker.port.start(); // 父级线程中的调用
port.start(); // worker线程中的调用, 假设port变量代表一个端口
postMessage()
方法必须被端口对象调用。
首先,在主线程中,向 worker 发送消息:
// main.js
myWorker.port.postMessage();
在 worker 中,接受消息的流程会比较复杂:
onmessage
事件处理函数,或者显式调用 start()
方法时),使用 onconnect
事件来执行函数event.ports[0]
)onmessage
(隐式的打开了与主线程的端口连接)// worker.js
onconnect = function (e) {
var port = e.ports[0];
port.onmessage = function (e) {
var workerResult = "Result: " + e.data[0] * e.data[1];
port.postMessage(workerResult);
};
};
最后,在主线程中接收消息:
// main.js
myWorker.port.onmessage = function (e) {
result2.textContent = e.data;
console.log("Message received from worker");
};
因篇幅问题不能全部显示,请点此查看更多更全内容