1 引言

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

2 概述

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

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

  1. 高效。
  2. 并行。

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

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

创建 Web Workers

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

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

收发消息

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

主线程接收发 Web Workers 消息

const worker = new Worker("../src/worker.js");worker.onmessage = e => {};worker.postMessage("Marco!");
复制代码

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

Web Workers 收发主线程消息

self.onmessage = e => {};self.postMessage("Marco!");
复制代码

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

销毁 Web Workers

worker.terminate();
复制代码

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

3 精读

对象转移(Transferable Objects)

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

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

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

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

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 形式内联在源码中。

import Worker from "worker-loader!./file.worker.js";const worker = new Worker();
复制代码

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

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

Blob URL

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

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

如果你想参与讨论,请点击这里,每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。

精读《谈谈 Web Workers》相关推荐

  1. 【韩松】Deep Gradient Comression_一只神秘的大金毛_新浪博客

    <Deep Gradient Compression> 作者韩松,清华电子系本科,Stanford PhD,深鉴科技联合创始人.主要的研究方向是,神经网络模型压缩以及硬件架构加速. 论文链 ...

  2. 【韩松】Deep Gradient Comression

    <Deep Gradient Compression> 作者韩松,清华电子系本科,Stanford PhD,深鉴科技联合创始人.主要的研究方向是,神经网络模型压缩以及硬件架构加速. 论文链 ...

  3. [文献阅读] Sparsity in Deep Learning: Pruning and growth for efficient inference and training in NN

    文章目录 1. 前言 2. Overview of Sparsity in Deep Learning 2.1 Generalization 2.2 performance and model sto ...

  4. 【翻译】Batch Normalization: Accelerating Deep Network Trainingby Reducing Internal Covariate Shift

    Batch Normalization: Accelerating Deep Network Trainingby Reducing Internal Covariate Shift Sergey I ...

  5. 模型加速--CLIP-Q: Deep Network Compression Learning by In-Parallel Pruning-Quantization

    CLIP-Q: Deep Network Compression Learning by In-Parallel Pruning-Quantization CVPR2018 http://www.sf ...

  6. 论文笔记30 -- (视频压缩)【CVPR2021】FVC: A New Framework towards Deep Video Compression in Feature Space

    <FVC: A New Framework towards Deep Video Compression in Feature Space> CVPR 2021 的一篇Oral 提出了特征 ...

  7. 端到端图像压缩《Asymmetric Gained Deep Image Compression With Continuous Rate Adaptation》

    Asymmetric Gained Deep Image Compression With Continuous Rate Adaptation 一 简介 二 内容 2.1 目前方法的缺陷 2.2 整 ...

  8. 深度学习视频压缩1—DVC: An End-to-end Deep Video Compression Framework

    本文是第一篇端到端使用神经网络来进行视频压缩的论文, github地址:GitHub - GuoLusjtu/DVC: DVC: An End-to-end Deep Video Compressio ...

  9. 【论文阅读】Deep Compositional Captioning: Describing Novel Object Categories without Paired Training Data

    [论文阅读]Deep Compositional Captioning: Describing Novel Object Categories without Paired Training Data ...

  10. CVPR 2018 TRACA:《Context-aware Deep Feature Compression for High-speed Visual Tracking》论文笔记

    理解出错之处望不吝指正. 本文的模型叫做TRACA.模型中使用多个expert auto-encoder,在预训练阶段,每个expert auto-encoder针对一个特定类进行训练:在tracki ...

最新文章

  1. 10条PyTorch避坑指南
  2. 每日一皮:当你开始研究一个新项目时是这样的吗?
  3. java 接口 泛型示例,java泛型接口实现示例
  4. 丢失api-ms-win-crt-runtime-l1-1-0.dll
  5. stm32f103r6最小系统原理图_超强PCB布线设计经验谈附原理图
  6. 天天酷跑php源码_使用Java实现天天酷跑(附源码)
  7. 谷粒商城:09.商品服务 — 品牌管理
  8. Java实验报告(基础练习、选择循环、数组、字符串及类和对象)
  9. Python基础——isupper()方法和islower()方法
  10. 干部身份、三方协议、派遣证(转)
  11. 如何把视频生成二维码,手机扫一扫就可以看
  12. SEO优化 关键词部署策略
  13. Matlab视觉处理模块定位控制全向轮小车运动:目标跟踪测试
  14. oracle如何获取xml节点,oracle解析xml,带命令空间的节点获取
  15. 【懒懒的Python学习笔记九】
  16. MyBatis 框架的思想及其第一次使用
  17. 编写一个程序,根据用户输入的英文名和姓先显示姓氏,其后跟一个逗号,然后显示名的首字母,最后加一个点:
  18. HTML微博浮动代码,jQuery实现仿新浪微博浮动的消息提示框(可智能定位)
  19. LTE:PHICH物理层处理过程及资源映射
  20. 5. VOLO: Vision Outlooker for Visual Recognition

热门文章

  1. 苹果 App Store 申请和管理相关知识
  2. tomcat无法启动(JVM错误)
  3. .Net页面的生命周期(ZZ)
  4. MobaXterm通过SSH连接centOS7
  5. 北斗导航 | RDSS短报文之数据解压缩算法:LZ77
  6. 数字语音信号处理学习笔记——语音信号的数字模型(3)
  7. 字符编码转换_进制转换(GB2312,GBK,JNI,HexTOStr)
  8. MFC CListCtrl 取消选中
  9. php 假多态,论PHP面向对象之-多态
  10. queue源码java_看看AbstractQueue源码Java9