跳到主要内容位置

2分钟了解 Web Worker

Web Worker 是浏览器内置的 API,用于在单独的线程中执行 JavaScript 代码,从而避免 UI 主线程阻塞,影响页面响应。

我们知道 JavaScript 是单线程的语言,如果代码中有耗时的同步代码,那么页面会失去响应,极大的影响用户体验:

let arr = [4, 3, 1, 9];

sortBtn.addEventListener("click", () => {
for (let i = 0; i < 9999999999; i++) {}
console.log("for 执行完毕");
resultArr.textContent = arr.sort((a, b) => a - b));
});

alertBtn.addEventListener("click", () => {
alert("test");
});

而使用 web worker,可以单独开启一个线程,线程之间通过 message 消息进行通信,web worker 中的代码执行不会影响 UI 的响应:

let worker = new Worker("worker.js");

sortBtn.addEventListener("click", () => {
worker.postMessage(arr);
});

worker.onmessage = function (e) {
resultArr.textContent = e.data;
};
onmessage = function (e) {
for (let i = 0; i < 9999999999; i++) {}
const data = e.data;
postMessage(data.sort((a, b) => a - b));
};

需要注意的是 web work 线程不能访问 window 等全局属性,也不能修改 DOM,但可通过 XMLHttpRequest 或 fetch 发送网络请求。如果要修改 UI,可以给主线程发送消息,通知主线程修改。

给 web worker 传递消息或从 web worker 接收消息,消息中的数据是深度拷贝的:

arr;

定义 web worker

要定义 web worker,可以直接新建一个普通的 js 文件,在里边监听 onmessage 事件,通过事件参数的 data 属性访问传递进来的消息,然后使用 postMessage 回传消息给主线程,因为 web worker 的全局变量指向的是它本身,所以这里可以直接访问 onmessage 或 postMessage 等属性:

onmessage = function (e) {
const data = e.data;
postMessage(data.sort((a, b) => a - b));
};

在主线程 JS 中,也就是直接通过 HTML 引入的 JS 文件里,使用 Worker() 构造函数,传递 web worker 的 url 路径,这里必须是同源的,之后保存返回的实例:

let arr = [4, 3, 1, 9];
let worker = new Worker("worker.js");

通过 web worker 实例同名的 postMessage 方法,可以给 web worker 发送消息:

sortBtn.addEventListener("click", () => {
worker.postMessage(arr);
});

同理,监听 web worker 实例的同名 onmessage 事件,也可以接收 web worker 发送过来的消息:

worker.onmessage = function (e) {
resultArr.textContent = e.data;
};

示例

这里我们的示例里,使用了 web worker 来对数组进行排序,在 HTML 中,我们显示了当前数组、排序按钮和结果数组:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="style.css" />
<title>Document</title>
</head>
<body>
<h1>index.html</h1>
<p id="sourceArr"></p>
<button id="sortBtn">排序</button>
<p id="resultArr"></p>
<script src="index.js"></script>
</body>
</html>

之后在主线程 index.js 里,初始化了 web worker,监听排序按钮的点击事件,给 web worker 传递消息,内容是需要排序的数组:

const sourceArr = document.getElementById("sourceArr");
const sortBtn = document.getElementById("sortBtn");
const resultArr = document.getElementById("resultArr");

let arr = [4, 3, 1, 9];

sourceArr.textContent = arr.toString();

let worker = new Worker("worker.js");

sortBtn.addEventListener("click", () => {
worker.postMessage(arr);
});

之后通过 web worker 实例监听 onmessage 事件,接收 web worker 传递回来的已经排好序的数组:

worker.onmessage = function (e) {
// worker.port.onmessage = function (e) {
resultArr.textContent = e.data;
};

而在 web worker 中,我们调用数组的 sort 方法对数组进行排序,然后把排序后的数组传递回主线程。

onmessage = function (e) {
const data = e.data;
postMessage(data.sort((a, b) => a - b));
};

这样就实现了在 web worker 中执行排序代码。

共享 web worker

同一个 web worker 实例还可以在不同的页面或浏览器窗口中共享,它叫做共享 web worker,刚才介绍的 web worker 是独享的,每次调用 new Worker() 会创建新的 web worker 线程。共享的 web worker 需要使用 port 对象来访问 onmessage 和 postMessage,因为共享的 web worker 需要根据端口号来进行通信,端口的管理不需要我们自己操作,只需要使用 port 对象就可以了:

onconnect = function (e) {
const port = e.ports[0];

port.onmessage = function (e) {
// 高亮
const data = e.data;
port.postMessage(data.sort((a, b) => a - b));
};
};

在共享的 web worker 里我们需要监听 onconnect 事件,来获取 port 对象,之后利用它来监听 onmessage 和调用 postMessage:

onconnect = function (e) {
// 高亮
const port = e.ports[0];

port.onmessage = function (e) {
const data = e.data;
port.postMessage(data.sort((a, b) => a - b));
};
};

在不同页面的 js 文件中,可以调用 SharedWorker 来创建共享 web worker 实例,后续发送消息和监听消息,都需要使用实例的 port 属性来访问:

// index.js
let worker = new SharedWorker("sharedWorker.js");
sortBtn.addEventListener("click", () => {
worker.port.postMessage(arr);
});

worker.port.onmessage = function (e) {
resultArr.textContent = e.data;
};

// index2.js
let worker = new SharedWorker("sharedWorker.js");
sortBtn.addEventListener("click", () => {
worker.port.postMessage(arr);
});

worker.port.onmessage = function (e) {
resultArr.textContent = e.data;
};

小结

好了,这个就是 web worker 的作用和使用方法,你学会了吗?如果有帮助请三连并关注,想学更多的开发知识,可以在评论区留言,感谢观看!

提示

一系列的课程让你成为高级前端工程师。课程覆盖工作中所有常用的知识点和背后的使用逻辑,示例全部都为工作项目简化而来,学完即可直接上手开发!

即使你已经是高级前端工程师,在课程里也可能会发现新的知识点和技巧,让你的工作更加轻松!

《React 完全指南》课程,连载中现只需 48 元(领取优惠券)点击查看详情。

《Vue 3.x 全家桶完全指南与实战》课程,包括 Vue 3.x、TypeScript、Vue Router 4.x、Vuex 4.x 所有初级到高级的语法特性详解,让你完全胜任 Vue 前端开发的工作。点击查看详情。

《React即时通信UI实战》课程,利用 Storybook、Styled-components、React-Spring 打造属于自己的组件库。

《JavaScript 基础语法详解》本人所著图书,包含 JavaScript 全面的语法知识和新特性, 可在京东、当当、淘宝等各大电商购买