写点什么

聊聊 Web Workers 吧

用户头像
Faye
关注
发布于: 18 小时前
聊聊 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

发布于: 18 小时前阅读数: 10
用户头像

Faye

关注

less is more. 2018.11.08 加入

想学的很多,想分享的更多,因为有我的思考。

评论

发布
暂无评论
聊聊 Web Workers 吧