原文链接: Web Workers in JavaScript [A Complete Guide] - 原文作者: Bala Krishna Ragala
本文采用的是意译的方式
JavaScript
被设计来在浏览器运行小块的脚本。因为该语言的简单和直白,JavaScript
备受欢迎,在当时,没有人能够预料它能执行大量的计算和对 CPU
要求较高的任务。这是一门深受初学者程序员喜爱的语言,帮助他们进入编程中的"逻辑构建"世界。在过去的 20
年时间,JavaScript
正在主导现代世界的整个 Web
应用业务。从操作前端的的 Dom
到涉及后端的服务,都能通过 JavaScript
来完成整个业务。
JavaScript 中的 Web Workers 是什么?
JavaScript
中的 Web Worker
允许我们在不干扰用户界面的前提下,Web
内容在后台运行。JavaScript 是单线程的语言。 所以,所有的脚本执行都是通过这个单线程执行,称为主线程。主线程通常负责进行所有繁重的工作。当主线程已经忙于执行其他 CPU
密集型进程时,当你尝试执行一个进程时,可能会出现问题。这就是 HTML5
中 Web Workers
发挥作用的地方。使用 Web Workers
,我们可以将一些进程从主线程转移到 Web Worker
线程中。这就释放主线程去做其他的任务,因为这个时候 Worker
线程在处理 CPU
密集型的任务。
Web Worker - 案例
HTML Web Workers
通常用来卸载任务。它们被用来卸载主线程的任务,以方便主线程可以立马执行其他任务。
- 它可以用于执行一些对
CPU
要求高的任务。 - 它还用于在后台循环 URL。
- 在线上代码编辑器上,它被用来进行语法高亮功能。
总之,当我们想执行某个任务而不希望用户界面在任务执行期间保持卡住时,我们可以使用 HTML Web Workers
。
Web Workers 通信
为了从主线程卸载(转移)任务到 worker
线程中,然后再将 worker
中的结果返回主线程中,我们需要在两个线程中建立某种通信渠道,或者说是脚本。
我们可以通过下面两种方法在 main
脚本和 worker
脚本中建立通信渠道:
1. postMessage() Method
这个方法,我们创建了一个分离的脚本,并在 main
的脚本中,使用 .postMessage()
和 onmessage()
的事件监听器将其联系起来。我们也使用 Worker
类构建器创建其对象,然后定义 worker
脚本的引用路径。
javascript
const worker = new Worker(worker.js);
Sending and Receiving a message from the main script to the worker script:
// main.js
btn1.addEventListner('click', (event) => {
worker.postMessage('Hello Worker'); // Using the postMessage to send Message from the main.js to worker.js
})
worker.onmessage = function(message);
alert(`The sum is: ${message.data}`);
worker
脚本从 main
脚本中接收信息:
javascript
// worker.js
self.onmessage = function(message){ // Using onmessage to receive the message in the worker.js
let sum = 0;
for(let i = 0; i < 10000000l i += 1)
sum += 1;
self.postMessage(sum); // Using postMessage to send the answer back to the main.js
}
2. 广播通道
广播通道,就像我们所知道的广播通道接口(Broadcast Channel API),允许浏览器上下文基本的通信(比如 windows, frames, tabs, 或者 iframes)。与 worker
通信,我们创建一个 BroadcastChannel
对象。我们想要进行通信的 frames
或者 workers
,不需要保存在我们的引用列表中,因为它们可以通过使用相同的名称创建它们的 BroadcastChannel
,并在它们之间实现双向通信。
A) 创建 Broadcast 通道
javascript
const bc = new BroadcastChannel('newChannel');
B) 发送一条信息
javascript
bc.postMessage('Message is sent through a broadcast channel');
C) 接受一条信息
javascript
bc.onmessage = (event) => { console.log(event) };
D) 断开通道
javascript
bc.close();
Web Worker API
正如上面我们提及到的引入 worker
的脚本路径那样,worker
就是通过使用 Worker
类构造函数创建的一个对象。worker
脚本中包含了将运行的 worker
线程。Web Worker
有两种类型:
1. Dedicated Workers(专用)
Dedicated Workers
是由一个脚本利用 DedicatedWorkerGlobalScope
对象表示的上下文的工作线程。Dedicated Workers
专用线程只能被调用它的脚本访问。
javascript
const worker = new Worker('worker.js');
上面👆创建了一个专用的 web worker
。
2. Shared Workers(共享)
能够被多个脚本使用,只要它们与工作线程在相同的域名中并且位于不同的 windows
, IFrames
等等,就能被称为 Shared Workers
。Shared Workers
比 Dedicated Workers
复杂些,因为在 shared workers
中,脚本必须通过一个活动的端口进行通信。
javascript
const worker = new SharedWorker('worker.js');
上面创建了一个共享的 web worker
。
Web Worker 在 JavaScript 是怎么工作的?
我们以一个例子来说明:
假设在页面中有两个按钮。一个按钮是 Run Calculate function
,另一个是 Change the background
按钮。Run Calculate function
按钮将会执行前十亿数字的总和。然后 Change the background
按钮将触发将更改页面的背景颜色。
我们假设计算前十亿数字的总和是一个 CPU
密集型的任务。如果我们在同一个线程里面执行两个按钮事件。那么,更改按钮的事件会在执行前十亿数字的总和事件 之后执行。我们不能做任何事情,直到 calculate()
函数完成计算,这将导致我们的网页变得缓慢和不流畅。
为了解决这个情景,我们使用 worker.js
来执行该 CPU
密集型任务。下面是一个 javascript web worker
的例子:
译者加:读下面的代码,即可知道执行的过程
javascript
//main.js
const worker = new Worker(worker.js) // creating a new instance of the Worker class. // And also defining the path of the worker script where the code that needs to be offloaded will be kept. This script is called a worker script const sumBtn = document.querySelector("#sumBtn"); const bgBtn = document.querySelector("#bgBtn");
sumBtn.addEventListner('click', (event) => {
worker.postMessage('Hello Worker'); // We use postMessage to talk between main script (thread) and worker script (thread).
});
worker.onmessage = function(message){
alert(`The final sum is ${message.data}`);
} // Receiving the message from the worker.js script
bgBtn.addEventListener('click', (event)=>{
if(document.body.style.background !== "blue")
document.body.style.background = "blue";
else
document.body.style.background = "green";
});
//worker.js
self.onmessage = function(message){
let sum = 0;
for(let i=0; i<10000000000; i++)
sum += i;
self.postMessage(sum); // This'll return the sum back to the main thread
}
我们什么时候需要使用 JavaScript Web Workers?
Web workers
可以在我们需要多线程执行功能的任何地方使用,比如下面的情况:
- 仪表盘页面,用于展示实时数据
- 展示股票价格,实时的用户(比如在
Hotstar
中观看比赛时) - 线上代码编辑器中语法高亮
- 自动保存功能
- 拼写检查功能
JavaScript Web Worker 局限性
Web worker
是一个强大的工具;然而,它有些局限性:
- 它不能直接操作
DOM
- 它对
window
对象的方法和属性访问有限制 - 它不能直接从文件系统运行,只能通过服务器运行
总结
在这个教程中,我们需要的了 web worker
是什么,我们怎么在真实的复杂网页中应用它,怎么在两个线程中发送信息,JavaScript
作为脚本语言的一些局限性。
Web Worker
的方法可以帮助解决复杂的网页应用程序客服这个限制,为流畅的用户体验和平滑的工作流打开新的可能性。
FAQ
1. Web Worker 被用来做什么?
Web Worker
通常执行卸载任务。它们被用于卸载主线程任务,以便主线程可以立即执行其他任务。
- 它可以被用来执行一些
CPU
密集型工作。 - 它还用于在后台循环 URL。
- 在线上代码编辑器上,它被用来进行语法高亮功能。
2. Web Worker 是一个线程?
Web Worker
是一种方式,通过它我们可以在 JavaScript
代码中添加多线程功能。
3. 有多少个 Web Worker 可以同时运行 JavaScript?
这取决于用户的系统。浏览器每个 tab
都会创建一个线程。主线程可以创建无数个 Web Workers
,直到用户系统资源被完全消耗。
4. Service Worker 和 Web Worker 的区别?
Service Worker
允许用户通过 fetch
事件监测网络请求,并通过 push
事件在后台监听 Push API
事件。而 Web Worker
做不到。
5. 我们怎么对 Web Worker 进行 Debug?
我们可以使用 chrome
内置的 debugger
工具来 debug
web worker
。 当然,其他的主流浏览器也会有。