JavaScript 周报 436 期之 WebWorker 是什么
本周 JavaScript 436期周报链接。这里面主要我想谈谈 WebWorker 相关的内容。
众所周知,JavaScript 使用运行在单线程上的。但是 JavaScript 可以处理异步代码,其中 Ajax 就是个很好的例子。异步带来的用户体验不必多说,但是这里有个问题:如果异步之后的成功回调是一次密集 CPU 操作。那么本来因为异步而产生的非阻塞用户体验,就又会因为无法从事件循环出解脱而又产生阻塞。对用户而言便又产生了不响应的 UI。这种情况,我们可以认为 JavaScript 只是解决了单线程的部分限制。可以假设,如果我们把这一次密集 CPU 操作也在异步中执行,那么用户体验仍然一如既往地流畅。
Web Worker 就应用而生了。
Web Workers 概述
Web Worker 是真正的多线程。
Web Worker并不会阻塞事件循环。参考这个 demo 可以看到在 5 万条数据排序的时候,使用 WebWorker 带来的体验差别。Web Worker 并不是 JavaScript 的一部分,它是通过 JavaScript 可以访问的浏览器特性。然而,Web Worker 并没有在 Node.js 中实现,有点像子进程的概念,但是却有不同。
Web Worker是有三种类型,在 MDN 中有详细定义:
- 专用 Workers
- 共享 Workers
- 服务 Workers
其中,专用 Workers 是通过主进程实例化,也只能通过主进程通信;共享 Workers 可以访问所有同源的进程(在不同的浏览器标签,窗口或者其他的共享 Workers);服务 Workers 是针对源和路径的事件驱动 worker,用在网络不可用的情况下,可以控制网站和相关站点,插入和修改导航和资源请求,缓存资源等。
如何工作
Web Workers 在浏览器中运行一个独立的线程。正因如此,执行的代码需要被包含在一个独立的文件中,这一点比较重要。使用方式也很简单:
var worker = new Worker('taks.js');
浏览器会创建一个异步下载的文件线程。当下载完成后,会被执行。通过调用 postMessage
方法来使用创建好的 worker。它可以让一个 Web Worker 和页面之间通信。
如何使用 postMessage 方法
比较新的浏览器支持 JSON 作为第一个参数,而旧的浏览器只支持字符串。
<button onclick="startComputation()">Start computation</button>
<script>
function startComputation() {
worker.postMessage({'cmd': 'average', 'data': [1, 2, 3, 4]});
}
var worker = new Worker('doWork.js');
worker.addEventListener('message', function(e) {
console.log(e.data);
}, false);
</script>
在 new 一个 Worker 之后,给它的实例注册一个叫 message
的监听事件。同时在我们的 doWork.js
里,也要注册相同事件用来处理数据。
self.addEventListener('message', function(e) {
var data = e.data;
switch (data.cmd) {
case 'average':
var result = calculateAverage(data); // Some function that calculates the average from the numeric array.
self.postMessage(result);
break;
default:
self.postMessage('Unknown command');
}
}, false);
当消息到达的时候,worker 就开始执行计算,具体来说运行 calulateAverage
方法。结束之后,把结果传递给主页面。在 worker 的上下文环境中,self
和 this
都是 worker 的全局,与 window 无关。有两种方式可以结束 worker: worker.terminate()
和 self.close()
。
Web Worker 可用特性
navigator
对象location
对象(只读)XMLHttpRequest
setTimeout()/clearTimeout()
和setInterval()/clearInterval()
- 应用缓存
- 使用
importScripts()
导入外部脚本 - 创建其他的 web workers
Web Worker 限制
- DOM(很显然,多线程的操作DOM是不合理和不安全的)
window
对象document
对象parent
对象
几个 Web Workers 的场景
-
光线追踪
为了模拟光线路径,这种场景是需要大量计算的,但是不能再每次大量计算的时候就让光线变得卡顿或者不自然,这时候workers 就可以发挥作用了。
-
加密
也是纯粹的计算工作,交给 web workers 正当其用。而且有些加密解密的操作是相当消耗时间的。
-
预加载数据 当网络不是很稳定的时候,可以先把数据取出来存在本地浏览器缓存中,这样在需要的时候就会减少延迟感
-
拼写检查
如果对应的检查数据是一本字典的话。这种场景下,使用 web worker 就十分必要了。
总结一下使用 Web Workers 的场景的共同点:
- 延迟甚至不需要 DOM 操作的情况
- 短时间使用产生大量计算,会阻塞 UI 线程,导致卡顿的场景
- 预处理,异步处理的部分场景
小结
Web Worker 是个浏览器实现的特性,尽管如此,我们还是能够在某些地方发挥它的优势去增强用户体验,遵循“渐进增强,平稳退化”得原则。没有理由去拒绝使用它。但其实,Web Worker 的使用场景也十分具有特点,它应该不是作为解决卡顿问题的首选,因为在前端,除了动画以外往往没有特别大的计算量。大多数的不流畅用户体验,多半是重绘和重排没有优化好而引起的,千万不可缘木求鱼,为了 Web Worker 什么都往上鼓捣。
参考资料

转载自:https://juejin.cn/post/6844903845307531271