为什么需要 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数量。