JavaScript 中 Web Workers【完整指南】

原文链接: 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 密集型进程时,当你尝试执行一个进程时,可能会出现问题。这就是 HTML5Web 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 WorkersShared WorkersDedicated 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。 当然,其他的主流浏览器也会有。

相关推荐
代码小鑫5 分钟前
A032-基于Spring Boot的健康医院门诊在线挂号系统
java·开发语言·spring boot·后端·spring·毕业设计
豌豆花下猫22 分钟前
REST API 已经 25 岁了:它是如何形成的,将来可能会怎样?
后端·python·ai
NoloveisGod23 分钟前
Vue的基础使用
前端·javascript·vue.js
GISer_Jing24 分钟前
前端系统设计面试题(二)Javascript\Vue
前端·javascript·vue.js
喔喔咿哈哈35 分钟前
【手撕 Spring】 -- Bean 的创建以及获取
java·后端·spring·面试·开源·github
夏微凉.1 小时前
【JavaEE进阶】Spring AOP 原理
java·spring boot·后端·spring·java-ee·maven
海上彼尚1 小时前
实现3D热力图
前端·javascript·3d
杨过姑父1 小时前
org.springframework.context.support.ApplicationListenerDetector 详细介绍
java·前端·spring
理想不理想v1 小时前
使用JS实现文件流转换excel?
java·前端·javascript·css·vue.js·spring·面试
惜.己1 小时前
Jmeter中的配置原件(四)
java·前端·功能测试·jmeter·1024程序员节