web worker 实践
介绍
通过使用Web Workers,Web应用程序可以在独立于主线程的后台线程中,运行一个脚本操作。这样做的好处是可以在独立线程中执行费时的处理任务,从而允许主线程(通常是UI线程)不会因此被阻塞/放慢,从页实现多线程。
主线程 => 创建 worker (new Worker(同源js链接)) -> 发送通知postMessage -> 接收通知onmessage -> 关闭close
worker线程 => 程序逻辑 -> 接收命令onmessage -> 处理数据 -> 发送通知postMessage -> 关闭terminate
不可使用点
- 与主线程不在同一个上下文
- 不可以操作 DOM,一切与 DOM 操作相关的函数、类都不能使用
window对象的某些属性和方法, 重点注意XMLHttpRequest只会返回null
Blob 使用
学习参考:动态创建 Web Worker 实践指南
Web Worker 常用构造函数来加载 js 链接文件,而我们知道:
Blob对象是一个不可变、原始数据的类文件对象,但不局限于 JavaScript 原生格式的数据,常被用来存储体量很大的二进制编码格式的数据,因此我们可以存入worker.js的逻辑代码。URL.createObjectURL则可以创建链接。
因此我们有了这样的例子:
1 | const response = "onmessage=function(e){postMessage('Worker: '+e.data);}"; |
Promise 实现
学习参考: 使用 Web Worker 实现简单的非阻塞异步
setTimeout 和 Promise 是阻塞异步的, 当然也包括 await 的方式,这要求主线程等待。
在 web worker 中, postMessage 和 onmessage 是可以一一对应的,我们可以用一个唯一ID来匹配。
1 |
|
动态创建 web worker
通过以上的认识,我们可以创建一个类,构建 postMessage 和 onmessage 的接口,在初始化时传入 worker.js 的逻辑代码。
方式一: 传入字符串
1 | const util = ` |
方式二: 传入函数
原因: Function.prototype.toString(): 返回一个表示当前函数源代码的字符串
1 | const util = (method, ...args) => { |
难点:如何将 js 代码以字符串传入 web worker
方式一: worker-loader 以内联方式打包:
1 | { |
方式二: webpack 以字符串方式加载js文件:
1 | import file from '!raw-loader!file.js' |
方式三:nodejs 使用 fs 在读取编译后文件以字符串输出
内联Blob 和 Promise 的不足
Promise只能响应处理对应的单条信息,持续响应还是需要onmessage原生方式 或Proxy代理等方式实现- 内联
Blob增加了项目初始大小
扩展阅读:
最佳实践: 谷歌出品的 comlink
使用 Promise 和 Proxy 实现
实践demo: gcid.ts 和 gcid.worker.ts
注意:proxy 要与对应的方法传回调方法,不要传对象