首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

聊聊 Web Workers 吧

背景

为啥突然想学习 Web Worker 了呢,因为我在某金上看到了一篇文章,而那篇文章有个评论说 Web Worker 实现起来很丝滑,我在想是要怎么实现呢,于是就开启了探究 Web Worker 之旅……

阅读 MDN

以下是 MDN 的相关解释:

用 Web Workers,Web 应用程序可以在独立于主线程的后台线程中,运行一个脚本操作。这样做的好处是可以在独立线程中执行费时的处理任务,从而允许主线程(通常是 UI 线程)不会因此被阻塞/放慢。

显然使用 Web Worker 是有好处的,就是可以将任务重的计算在不阻塞主线程的情况下继续运行,实现性能的提升(不过也不宜过度使用,后面会说到)。

Web Worker 可分为 Dedicated Workers、Shared Workers、Service Workers、Chrome Workers 以及音频 Workers,后面两个我负责的项目中使用场景不多,就不展开了,主要介绍前三个 Web Workers。

Dedicated Workers

定义、使用

Dedicated Workers 使用构造函数 Worker() 创建一个 Worker 对象,构造函数接受一个 JavaScript 文件 URL,这个文件包含了将在 worker 线程中运行的代码,具体例子:

// main.jsvar myWorker = new Worker('./worker.js');
// worker.js,里面是worker线程运行的任务(执行的计算比较重的代码)onmessage = function(e) {  console.log('Message received from main script');  var workerResult = 'Result: ' + (e.data[0] * e.data[1]);  console.log('Posting message back to main script');  postMessage(workerResult);}

复制代码

启动/运行 Web Worker

首先要知道 Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络。所以如果本地创建了一个 html 文件和 js 文件,直接在浏览器打开该 html 文件,Web Worker 是无效的,比如我打开的是file:///Users/xxx/Desktop/aaa/aaa.html,内部引入的 js 包含了上面 Web Worker 相关的 JS 代码,那么控制台会报出一个错误

所以如果想在本地调试,需要将文件 serve 起来,我是用的是 python 命令:python -m SimpleHTTPServer 8000

Chrome Debug

代码上实现了 Web Worker,也确实能正常运行,那么我怎么知道 worker 到底在哪里呢,我的页面里面有几个 workers 呢?

打开 Chrome -> CMD+SHIFT+I -> Sources Tab -> Page,然后就可以看到有多少个 Web Worker 以及具体 Web Worker 的脚本。

关闭 Web Worker

使用完毕,为了节省系统资源,必须关闭 Worker。

// 主线程worker.terminate();// Worker 线程self.close();

复制代码

对于 Dedicated Workers 而言,关闭意味着在 chrome 的 sources tab 也会消失:

Shared Workers

Shared Workers 跟 Dedicated Workers 使用上是基本一致的,可能更复杂一些,Shared Workers 可被不同的窗体的多个脚本运行,例如 IFrames 等。代码举例🌰:

// main.jsif (!!window.SharedWorker) {  var myWorker = new SharedWorker("worker.js");
  first.onchange = function() {    myWorker.port.postMessage([first.value, second.value]);    console.log('Message posted to worker');  }
  second.onchange = function() {    myWorker.port.postMessage([first.value, second.value]);    console.log('Message posted to worker');  }
  myWorker.port.onmessage = function(e) {    result1.textContent = e.data;    console.log('Message received from worker');    console.log(e.lastEventId);  }}
// worker.jsonconnect = function(e) {  var port = e.ports[0];  port.onmessage = function(e) {    var workerResult = 'Result: ' + (e.data[0] * e.data[1]);    port.postMessage(workerResult);  }}

复制代码

在 Chrome 的 debug 方式也不一样,打开chrome://inspect,可以看到很多 navigations,其中有一个叫做Shared Workers,那这里是可以看到打开的网页有哪些是使用了 Shared Workers 的。

Service Workers

说实话,我本人接触的第一个 Web Workers 就是 Service Worker,以前有一个项目是给工人在工地上做工时应用,工地也没有网络,或者说信号极差,所以对离线要求也比较高,当时调研过 Service Worker,但是使用起来有点复杂的,再加上当时不满足浏览器的兼容性,所以就使用了另外一种方式indexdb

根据文档,Service Worker 可以创建有效的离线体验,拦截网络请求,以及根据网络是否可用采取合适的行动,更新驻留在服务器上的资源。

Angular 框架是实现了 Service Worker 的,而其中也有一些 bug(记得是版本 7,现在有可能已经修复了,很久没用过 Angular 了😅),如果开启了之后,可能会对版本的更新有一定的影响,话说至今我都不明白为啥那个项目要开启 Service Worker,大家也米有离线的需求额……

在 Chrome 浏览器也是可以很方便地 debug,在 Application tab 的子菜单里面:

开发注意的地方/限制

  1. 前面也提到了,Web Workers 不能通过本地文件的方式运行,只能通过网络,否则无法执行;
  2. 同源:分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源;
  3. DOM 限制:可能很多人特别开心,那页面渲染的性能瓶颈是不是就能通过 Web Workers 来解决了呢,还是图样图森破啊,Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用 document、window、parent 这些对象。

但是还是有很多 Web API 在 Web Workers 中是可以访问的,比如使用 XMLHttpRequest 来访问网络,可以使用 navigator 对象和 location 对象等,所以别灰心,大多数 API 也是可以用的。

思考

虽然学习了 Web Workers,也知道如何使用,但是目前来讲好像使用 Web Workers 解决问题的项目不多,通过搜索引擎发现很多库/工具都实现了 Web Workers 呢,我个人还是很看好 Web Workers 滴:

🌟: useWorker

🌟: Threadjs

🌟: react-worker-image

🌟: worker-dom

欢迎大家一起交流哦🍺🍺🍺

References:

  • http://www.ruanyifeng.com/blog/2018/07/web-worker.html
  • https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API
  • https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Functions_and_classes_available_to_workers
  • 发表于:
  • 本文为 InfoQ 中文站特供稿件
  • 首发地址https://www.infoq.cn/article/d18548286d06e314d12db3afd
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券