前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >76.精读《谈谈 Web Workers》

76.精读《谈谈 Web Workers》

作者头像
黄子毅
发布2022-03-14 16:33:30
6080
发布2022-03-14 16:33:30
举报
文章被收录于专栏:前端精读评论

1 引言

本周精读的文章是 speedy-introduction-to-web-workers,是一篇 Web Workers 快速入门的文章,借精读这篇文章的机会,谈谈对 Web Workers 的理解与运用。

2 概述

就像分工,你只负责编码,而你的朋友负责设计,那你就可以专心把自己的事情做好,而且更快速的完成任务。

本文通过一个比方,描述了 Web Workers 的两大特征:

  1. 高效。
  2. 并行。

因为浏览器是单线程的,任何大量耗时的 JS 任务都会卡住界面,使浏览器无法响应任何操作,这样的用户体验非常糟糕。Web Workers 可以将耗时任务拆解出去,降低主线程的压力,避免主线程无响应。

但 CPU 资源是有限的,Web Workers 并不能增加总体运行效率,算上通信的损耗,整体计算效率会有一定的下降。

创建 Web Workers

代码语言:javascript
复制
const worker = new Worker("../src/worker.js");

上述代码中,worker 就是一个 Web Workers 实例,执行的代码是 ../src/worker.js 路径下的文件。

收发消息

Web Workers 用来执行异步脚本,只要掌握了它与主线程通信的方式,就可以在指定时机运行异步脚本,并在运行完时将结果传递给主线程。

主线程接收发 Web Workers 消息
代码语言:javascript
复制
const worker = new Worker("../src/worker.js");

worker.onmessage = e => {};

worker.postMessage("Marco!");

每个 worker 实例通过 onmessage 接收消息,通过 postMessage 发送消息。

Web Workers 收发主线程消息
代码语言:javascript
复制
self.onmessage = e => {};

self.postMessage("Marco!");

和主线程代码类似,在 Web Workers 代码中,也是 onmessage 接收消息,这个消息来自主线程或者其它 Workers。也可以通过 postMessage 发送消息。

销毁 Web Workers
代码语言:javascript
复制
worker.terminate();

文章内容就这么多,是不是有写太简单了呢!笔者结合自己的使用经验,再补充一些知识。

3 精读

对象转移(Transferable Objects)

对象转移就是将对象引用零成本转交给 Web Workers 的上下文,而不需要进行结构拷贝。

这里要解释的是,主线程与 Web Workers 之间的通信,并不是对象引用的传递,而是序列化/反序列化的过程,当对象非常庞大时,序列化和反序列化都会消耗大量计算资源,降低运行速度。

上面的图充分证明了,大对象传递,使用对象转移各项指标都优于结构拷贝。

对象转移使用方式很简单,给 postMessage 增加一个参数,把对象引用传过去即可:

代码语言:javascript
复制
var ab = new ArrayBuffer(1);
worker.postMessage(ab, [ab]);

浏览器兼容性也不错:Currently Chrome 17+, Firefox, Opera, Safari, IE10+。更具体内容,可以看 Transferable Objects: Lightning Fast!。

需要注意的是,对象引用转移后,原先上下文就无法访问此对象了,需要在 Web Workers 再次将对象还原到主线程上下文后,主线程才能正常访问被转交的对象。

如何不用 JS 文件创建 Web Workers

Web Workers 优势这么大,但用起来需要在同域下创建一个 JS 文件实在不方便,尤其在前后端分离做的比较彻底的团队,前端团队能控制的仅仅是一个 JS 文件。那么下面给出几个不用 JS 文件,就创建 Web Workers 的方法:

webpack 插件 - worker-loader

worker-loader 是一个 webpack 插件,可以将一个普通 JS 文件的全部依赖提取后打包并替换调用处,以 Blob 形式内联在源码中。

代码语言:javascript
复制
import Worker from "worker-loader!./file.worker.js";

const worker = new Worker();

上述代码的魔术在于,转化成下面的方式执行:

代码语言:javascript
复制
const blob = new Blob([codeFromFileWorker], { type: "application/javascript" });
const worker = new Worker(URL.createObjectURL(blob));
Blob URL

第二种方式由第一种方式自然带出:如果不想用 webpack 插件,那自己通过 Blob 的方式创建也可以:

代码语言:javascript
复制
const code = `
  importScripts('https://xxx.com/xxx.js');
  self.onmessage = e => {};
`;

const blob = new Blob([code], { type: "application/javascript" });
const worker = new Worker(URL.createObjectURL(blob));

看上去代码更轻量一些,不过问题是当遇到复杂依赖时,如果不能把依赖都转化为脚本通过 importScripts 方式引用,就无法访问到主线程环境中的包。如果真的遇到了这个问题,可以用第一种 webpack 插件的方式解决,这个插件会自动把文件所有依赖都打包进源码。

管理 postMessage 队列

为什么 postMessage 会形成队列,为什么要管理它?

首先在 Web Workers 架构设计上就必须做成队列,因为调用 postMessage 时,对应的 Web Workers 不一定完成了初始化,所以浏览器底层必须管理一个队列,在 Web Workers 初始化完毕时,依次消费,这样才能确保任何时候发出的 postMessage 都能被 Web Workers 接收到。

其次,为什么要手动维护这个队列,原因可能取决于如下几点:

  • 业务原因,前面的 postMessage 还没来得及消费,就不要发送新的消息,或者丢弃新的消息,这时候需要通过双向通信拿到 Web Workers 的执行结果回执,手动控制队列。
  • 性能原因,一般 Web Workers 都会被用来执行耗时的同步运算,如果运算时间比较长,那短期塞入多个消息队列是没有意义的。

如上图所示,对于每次用户输入都要进行的 SQL Parser 很耗时,及时放在 Web Workers 也可能导致将 Workers 撑爆到无响应,这是不仅要使用多 Workers 缓冲池,还要对待执行队列进行过滤,因为用户永远只关心最后一次输入的 Parser 结果。

由于 Web Workers 运算被卡住时,除了销毁 Worker 没有别的办法,而销毁 Worker 的成本比较高,不能对每一个用户输入都销毁并新建 Web Workers,所以利用 Workers 缓冲池,当缓冲池满了,新的消费队列又进来的时候,可以销毁全部 Workers 缓冲池,换一批新缓冲池重新消费用户输入。

4 总结

Web Workers 是拆解异步计算的好帮手,vscode 网页版也通过 Web Workers 异步完成代码提示和高亮,笔者有对比过,发现 Web Workers 性能提升非常明显。

管理好你的 Web Workers 消息队列,谨防同步计算让 Web Workers 失去响应,建立一个智能的消息队列,根据业务需求设计一个最好的队列消费模型吧!

5 更多讨论

讨论地址是:精读《谈谈 Web Workers》 · Issue #108 · dt-fe/weekly

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-08-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端精读评论 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 引言
  • 2 概述
    • 创建 Web Workers
      • 收发消息
        • 主线程接收发 Web Workers 消息
        • Web Workers 收发主线程消息
        • 销毁 Web Workers
    • 3 精读
      • 对象转移(Transferable Objects)
        • 如何不用 JS 文件创建 Web Workers
          • webpack 插件 - worker-loader
          • Blob URL
        • 管理 postMessage 队列
        • 4 总结
        • 5 更多讨论
        相关产品与服务
        文件存储
        文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档