Web Worker的使用

1,133 阅读3分钟

为什么需要 Web Worker

  • JavaScript语言的单线程模式限制,一次只能做一件事,无法并行操作,甚至影响用户体验
  • 随着前端的发展,计算密集型或高延迟任务越来越多,多核cpu的出现,单线程也无法发挥cpu的计算能力

使用场景

  • 预取数据,比如预先获取大量数据,存储本地IndexDB处理、SSE等
  • 密集运算,比如拖放手势、加密、文件解析等
  • 图像处理,比如webgl一些图形渲染技术、光线追踪等

类型

web worker类型很多,web worker是统称,JavaScript里面work对象是指Dedicated Workers

  • Dedicated Workers 只能主线程和worker线程通信
  • Shared Workers 同源的所有worker线程通信
  • Service Workers 客户端和服务端的代理,运行与浏览器后台

上下文

  • 必须与主线程同源,主线程和worker必须Message通信
  • 无法访问dom、window、document、parent等对象
  • 但是继承JavaScript子函数集,可以访问XMLHttpRequest、setTimeout、Cache等
  • worker特有的API,比如importScripts、close、self等

更多API查看:developer.mozilla.org/zh-CN/docs/…

安全

  • worker文件必须通过服务端,无法访问本地文件
  • worker与主线程必须是同源
  • worker与主线程通信的值是通过拷贝,多worker之间不会影响值
  • worker之间环境相互隔离

通信

主线程和子线程之间

  • onMessage、postMessage 事件的抽象集合MessagePort interface
  • onMessage event 参数对象的抽象集合MessageEvent interface

子线程和子线程之间

  • 通过 MessageChannel 类监听port
  • 通信还是onMessage、postMessage等事件

调度顺序之谜

全看cpu的心情(复杂的调度算法),多核和多线程,那么如何确保按顺序执行?或返回数据的顺序?

无法保证worker线程执行顺序,但是能确保返回数据顺序, 主线程通过Proxy来代理worker的通信,并返回Promise对象,执行时触发post message事件,worker通过proxy监听到message,获取需要执行的函数,并返回结果,主线程监听到message有event.data就resolve函数修改Promise状态

大家可以用下chrome封装的[comlink](https://github.com/GoogleChromeLabs/comlink)

机制

兼容性

有些 worker API 有些浏览器可能不一定,可以查询测试一下

性能比较

渲染20000个DOM,分2次渲染,每次10000个

总结

Web Worker独立与主线程的后台线程中,执行费时的处理任务,从而不会阻塞/放慢主线程(ui线程)。

现在很多技术里面也有使用,比如react service、pwa、pdf.js、web vscode、webgl 3D等,还有一些小的场景也可以尝试,比如下拉提示,输入关键词去服务端查询,防止输入卡住;大数据tree的处理,防止渲染过慢。

总结来说就是通过提升并行数来提升性能(主要是体验上,相同的逻辑和算法消耗的时间总体上不会减少)。

⚠️:Worker也不是越多越好,worker有固定消耗的启动时间,而且通信也需要时间消耗,根据具体的需求确定启动的worker数量。