Web前端入门第 86 问:JavaScript 中的 Web Worker 为什么能提升代码性能?

Web前端入门第 86 问:JavaScript 中的 Web Worker 为什么能提升代码性能?

    正在检查是否收录...

最初的 JS 执行代码都是一条线执行到底,当遇到比较耗时的操作时,比如大数组循环运算,就会导致页面卡着,就像假死一样。就像一个人在厨房烧菜一样,需要依次完成切菜、炒菜、装盘这些步骤,此过程中没办法同时做其他事情,必须按顺序执行每一个步骤。

Web Worker 赋予了 JS 分配任务的能力,在遇到复杂的计算型任务时,比如 canvas 图形图像处理(添加滤镜、矩阵变换等),此类不依赖 DOM 操作的计算型任务都可以交由 Web Worker 来处理,这样不会阻塞主线程的任务调度,从而提升前端的代码运行速度。

任务时序图

1

模拟耗时任务

看如下代码,使用一个超大的 for 循环,模拟 JS 中的耗时任务,让代码执行时主线程卡顿,还原假死现象:

<div id="output"></div> <button id="start">开始复杂任务</button> <script> (() => { let times = 0 const output = document.getElementById('output') function loop () { setTimeout(() => { times ++ output.innerText = times loop() }, 1000); } loop() document.getElementById('start').addEventListener('click', () => { let n = 0 console.time('任务耗时') for (let i = 0; i < 10000000000; i++) { n += i } console.timeEnd('任务耗时') }) })(); </script> 

执行结果:

2

可以看到点击

开始复杂任务

按钮时,在计时器的第 4 秒主线程卡主了将近 4 秒,然后再恢复运行,这就是单线程中的 JS 耗时任务导致的页面假死现象。

使用 Web Worker 解决耗时问题

看了上面的耗时任务导致页面假死,再使用 Web Worker 来重写一下上面代码:

main.html

<div id="output"></div> <button id="start">开始复杂任务</button> <script> (() => { let times = 0 const output = document.getElementById('output') function loop () { setTimeout(() => { times ++ output.innerText = times loop() }, 1000); } loop() const worker = new Worker('./worker.js') worker.onmessage = event => { // 子线程计算结果 console.log(event.data) console.timeEnd('任务耗时') } worker.onerror = event => { console.error('Worker 异常:', event) } document.getElementById('start').addEventListener('click', () => { console.time('任务耗时') worker.postMessage(10000000000) }) })(); </script> 

worker.js

// worker.js self.onmessage = event => { let n = 0 let max = event.data for (let i = 0; i < max; i++) { n += i } postMessage(n) } 

执行结果:

3

可以看到虽然任务耗时长短差不多,但是主线程在点击按钮之后并没有进入假死状态,定时器还是在顺利执行,所以 Web Worker 中运行的复杂任务并不会影响主线程的任务调度。

Web Worker 限制

在子线程中运行的代码,无法直接操作 DOM,无法访问 window/document 对象,也无法使用 localStorage 等,如果使用这些 API,代码将会报错:

4

for 循环优化

注意上述代码中的 max 变量,为什么需要一个变量来保存 event.data 值?而不是直接使用 event.data 循环?将 worker.js 改造一下,看看不同使用方式的任务耗时:

// worker.js self.onmessage = event => { console.time('max 循环耗时') let n = 0 let max = event.data for (let i = 0; i < max; i++) { n += i } console.timeEnd('max 循环耗时') console.time('Object 循环耗时') let m = 0 for (let i = 0; i < event.data; i++) { m += i } console.timeEnd('Object 循环耗时') postMessage(n) } 

main.html

// main.html (() => { const worker = new Worker('./worker.js') document.getElementById('start').addEventListener('click', () => { worker.postMessage(100000000) }) })(); 

执行结果:

5

可以明显看到,性能耗时相差将近 6 倍,这数字会随着对象属性越多,耗时越长!!

所以在循环中应当尽量避免读取对象属性,尽可能使用变量来做循环条件!!

写在最后

可以使用 Web Worker 同时启用多个工作线程,只是在任务调度的时候,需要注意响应结果的先后顺序是否对主线程的运行有影响。

一些复杂的计算任务(比如视频转码,图片压缩,图片处理等),都丢给子线程处理吧,咱们前端也可以玩玩多线程~~

 

文章首发于微信公众号【前端路引】,欢迎 微信扫一扫 查看更多文章。

Web前端入门第 86 问:JavaScript 中的 Web Worker 为什么能提升代码性能?

本文来自博客园,作者:前端路引,转载请注明原文链接:https://www.cnblogs.com/linx/p/19069469

 

  • 本文作者:WAP站长网
  • 本文链接: https://wapzz.net/post-27705.html
  • 版权声明:本博客所有文章除特别声明外,均默认采用 CC BY-NC-SA 4.0 许可协议。
本站部分内容来源于网络转载,仅供学习交流使用。如涉及版权问题,请及时联系我们,我们将第一时间处理。
文章很赞!支持一下吧 还没有人为TA充电
为TA充电
还没有人为TA充电
0
0
  • 支付宝打赏
    支付宝扫一扫
  • 微信打赏
    微信扫一扫
感谢支持
文章很赞!支持一下吧
关于作者
2.8W+
9
1
2
WAP站长官方

崩了!Nacos升级到3.0竟不能用了,哭死!

上一篇

wuhuacong(伍华聪)的专栏

下一篇
评论区
内容为空

这一切,似未曾拥有

  • 复制图片
按住ctrl可打开默认菜单