Web Workers 用法
new Worker(url)
,将会在 worker 线程中运行代码,该环境与主线程不同。
因为并不是浏览器环境,所以 DOM、window 是无法使用的。当然也有一些API可以使用 localstroage、websocket、indexDB、XMLHttpRequest 等是没问题的。
同时分为两种环境。
- 独享模式:DedicatedWorkerGlobalScope (一个专用线程对应一个主线程)
- 共享模式:SharedWorkerGlobalScope(一个共享线程对应多个主线程)
主线程和 worker线程 之间使用 postMessage()
方法来发送信息,通过 onmessage
这个事件监听来接收信息。
数据的传输方式为传递副本,而不是直接共享数据。所以也导致有时传递的损耗高于计算的损耗。
当然,也支持整体内存移交给 worker,不过这样使用的话,主线程就访问不了这块内容了。
worker
独享线程(Dedicated Web Worker),只能由创建时的主线程使用。
//html wk = new Worker('/static/workers/1190000020913212.js'); demo2.addEventListener('click', function(){ wk.postMessage(str) }) //1190000020913212.js onmessage = function(e) { var str = e.data; console.time('reportString'); console.log(str.length, str.split('').join('+').length) console.timeEnd('reportString'); }
Shared Worker
同域被多个窗口 多个脚本运行时,可以用于通信。
比如 iframe、标签页。
// html swkPIP = new SharedWorker('/static/workers/1190000020913212-SharedWorker-pip.js'); swkPIP.port.start(); swkPIP.port.postMessage(args) // worker.js var portArr = []; onconnect = function(e) { var port = e.ports[0]; portArr.push(port) port.addEventListener('message', function(e) { console.log(self, e, port) if(e.data.type == 'private'){ port.postMessage(['pip', e.data]); }else if(e.data.type == 'public'){ portArr.forEach(v=>v.postMessage(['pip', e.data])) }else if(e.data.type == 'publicNoSelf'){ portArr.forEach(v=>v!=port&&v.postMessage(['pip', e.data])) } }); port.start(); // Required when using addEventListener. Otherwise called implicitly by onmessage setter. }
从实现代码中可以看到。
Shared Worker 需要使用 prot
通道。还需要 start()
开启通道。
可以实现的效果
- 跨窗口通信
- 比如说有一些轮询的接口,在多窗口的场景中会形成密集的访问。如果我们把请求转入Shared Worker,Shared Worker中做节流。拉回数据再通知各个窗口。
- 比如用户在一个窗口中登录,把登录状态通知给其他窗口。
Service Workers
用于浏览器到服务器之前的代理服务。可以实现离线访问、拦截请求、更新缓存等。
navigator.serviceWorker.register('/static/js/sw-20190621.js')
其他事项
通过URL.createObjectURL()创建URL对象,可以实现创建内嵌的worker
开启worker需要一个同源的URL。有时我们也不需要一个这样的文件,那么我们就可以通过一个指向内存的URL。
// worker.js var str = ` var i = 0; function test(){ postMessage(++i); setTimeout(test, 1000); } test(); `; // html var blob = new Blob([str]); var wk = new Worker(window.URL.createObjectURL(blob));
通过转让对象来传递数据
默认情况下,主线程与 worker 之前的数据传递是通过拷贝,也就是JSON.stringify()之后再发送,接受使用JSON.parse()处理。
这样使用起来在会损耗一部分在序列化与反序列化中。
所以 worker 给我们提供了一种更高效的方式,将整块数据传入(整块内存移交,再源环境将不可访问)。
myWorker.postMessage(uInt8Array, [uInt8Array]);
通过postMessage
的第二个入参,我们把uInt8Array
对象整个发送出去。
执行环境的上下文
我们都知道 浏览器环境的上下文和Node环境的上下文是不一样的,其中有一些是浏览器环境中独有的。worker环境也是不一样。
其中 self 指向当前环境的 global,与 Window 不是同一个对象,而是 WorkerGlobalScope
。