web worker

web worker

众所周知,JavaScript 是单线程,无法进行并行操作;因而 H5 中增加了 Web Worker,提供多线程解决方案;Web Worker 允许一段 JavaScript 程序运行在主线程之外的另外一个线程中

主线程与 worker之间,以及 workerworker 之间通过 postMessage()message 来通信

web worker 分两种类型:一种是专用线程(dedicated web worker),另一种是共享线程(shared web worker);前者被创建它的页面访问,后者在多个页面间共享

构建

1
2
3
const worker = new worker(url, options); // 专用线程

const sharedWorker = new SharedWorker(url, options); // 共享线程
  • url 必须遵循同源策略

  • 可以通过 options 指定 worker 名称

通信

1
2
3
4
5
6
7
8
// main
const worker = new worker(url);
worker.postMessage(message, transferList);

// worker
self.onmessage = (event) = {
console.info(event.data);
}

主线程和 worker 线程之间通过通过互相调用 sendMessageonMessage 来通信的 MessagePort 对象进行通信;专用线程使用隐式的 MessagePort 实例,专用线程被创建时,MessagePort 的端口消息队列便被主动启用;这与共享线程的 start() 效果一样

MessagePort 通信方式是深拷贝,即是传值而不是地址,子线程对通信内容的修改,不会影响到主线程;浏览器内部

线程之间传输的数据类型可以是字符串、对象,也可以是二进制数据,如 FileBlobArrayBuffer

线程之间传输数据的方式有两种:一种是采用 MessagePort 方式进行通信(深拷贝),浏览器内部通过 structured clone algorithm ,将发送端的数据进行序列化并拷贝,接收端收到的数据进行反序列化,这样两端的数据在发生更改时不会相互影响;另一种采用 Transferable Objects 方式进行通信(转移),这种方式主要用于二进制数据的传递,它对数据不做任何拷贝而是直接将数据值的引用转移给数据接收端,而数据发送端不会再持有该数据的引用,这样可以防止出现多个线程同时修改数据

使用 JSON.stringify 序列化需要传输的数据,根据 Nolan Lawson 的测试结果,证明传输 stringify 后的数据比传输原始数据更优

调试

Chrome 中打开 chrome://inspect/#workers

异常处理

  • onerror

    event 对象中包含发生异常的文件(filename)、行号(lineno)以及相应的错误信息(message

    1
    2
    3
    worker.addEventListener('error', (event) = {
    console.info(`${event.filename}-${event.lineno}-${event.message}`);
    });
  • onmessageerror

    1
    worker.onmessageerror = (event) = { console.dir(event); };

结束

有三种方式结束 worker:

  • 主线程调用 worker.terminate()
  • worker 内部调用 self.close()
  • 关闭页面(捂脸)

sub worker

worker 中可以创建子 worker,但有两点需要留意:

  • worker 与父 worker 必须同源
  • workerurl 是相对 父 worker,而不是主线程

inline worker

需要及时释放 Blob URLwindow.URL.revokeObjectURL(blobURL)Chrome 中可以在 chrome://blob-internals/ 中查看所有的 Blob URLs

加载外部脚本

Worker 线程能够访问一个全局函数 importScripts() 来引入外部脚本,该函数可以接受多个 urlurl 必须保证同源
;如果加载失败,则后续代码无法执行。
脚本的执行顺序依照传入函数的顺序,但下载顺序不固定。这个过程是同步的,所有脚本下载并执行完,函数才会返回。

1
2
3
4
5
importScripts();

importScripts('foo.js');

importScripts('foo.js', 'bar.js');

worker 执行流程

具体参考 Web Worker在WebKit中的实现机制

局限

Worker 不同于 window,它运行在另一个全局作用域(DedicatedWorkerGlobalScopeSharedWorkerGlobalScope)中,有些 window 中的属性和方法无法在 worker 中访问,具体可以查看这篇文章

不允许访问的对象包括:DOMdocumentwindowparentLocalStorage

允许访问的对象包括:navigatorlocation(只读)XMLHttpRequestsetTimeout / setIntervalApplication Cache

扩展

  • worker 执行环境中 selfthis 都指向worker 的全局作用域,以下三种写法效果一样
  • self 也提供一系列接口,包括:self.JSONself.Mathself.console

应用

  • 数据运算

  • 大量数据的检索、分析

  • 图像处理

  • 音频 / 视频解码

参考