基础使用javascript
动态内联workerhtml
subworkerjava
你们好,今天在这里简单介绍一下web workers的基本使用。webpack
web workers可使得一些涉及复杂计算的逻辑在独立的线程运行,从而不会影响页面的性能,例如渲染、交互响应等。git
web workers中可使用大多数标准的JS特性,例如XMLHttpRequest等;固然web workers也有很多局限行,例如不能操做DOM、做用域是独立的等等。github
下面来一块儿看一个简单的web workers例子。web
咱们首先建立一个worker.js
的空文件。咱们将在其中编写worker的代码。chrome
而后咱们经过new Worker(workerFilePath)
的方式创建一个worker。浏览器
// index.html
const worker = new Worker('./worker.js');
复制代码
主线程和worker经过postMessage
和onmessage
来通讯。安全
首先,咱们在主线程中经过worker.postMessage
向worker发送消息。
// index.html
worker.postMessage('start');
复制代码
接着,咱们在worker.js
中定义onmessage
来处理接收到的消息:
// worker.js
onmessage = function (ev) {
if (ev.data === 'start') {
timedCount();
}
};
复制代码
onmessage
会收到一个MessageEvent
事件做为参数,其中的data
属性就是咱们在postMessage
中发送的数据。
一样的,咱们能够在worker.js
中使用postMessage
向主线程发送消息:
// worker.js
const timedCount = function timedCount() {
i += 1;
postMessage(i);
setTimeout(timedCount, 500);
};
复制代码
在主线程中咱们经过worker.onmessage
接受worker
发来的消息,并能够调用worker.terminate
来终止worker
:
// index.html
worker.onmessage = function onmessage(ev) {
document.getElementById('result').innerHTML = ev.data;
if (ev.data === 10) worker.terminate();
};
复制代码
固然,咱们可使用worker.js
建立多个worker:
// index.html
const worker1 = new Worker('./worker.js');
const worker2 = new Worker('./worker.js');
const anotherWorker = new Worker('./anotherWorker.js');
复制代码
咱们可使用importScript
在web workers中引入其它代码,importScripts
是同步加载代码的,加载的代码中暴露的全局对象可以被worker使用,例如:
// worker.js
importScripts('./foo.js', './bar.js');
foo();
bar();
// foo.js
function foo() {
console.log('foo');
};
// bar.js
function bar() {
console.log('bar.js');
};
复制代码
注意这里引入代码的路径是与当前worker代码所在的路径对应的。
因为在主线程和web workers之间传递数据须要通过拷贝过程,所以当咱们在postMessage
中发送大型数据时性能会收到影响。而可转让对象使得数据在上下文切换的过程当中没必要通过任何拷贝操做,从而大大提升了性能。
当数据被转移后,在原上下文中将被清除而不复存在:
// index.html
const worker = new Worker('./js/arrayBuffer.js');
const uInt8Array = new Uint8Array(1024);
for (var i = 0; i < uInt8Array .length; ++i) {
uInt8Array[i] = i;
}
worker.postMessage(uInt8Array.buffer, [uInt8Array.buffer]);
console.log(uInt8Array.byteLength); // 0
// arrayBuffer.js
onmessage = function (ev) {
const uInt8View = new Uint8Array(ev.data);
console.log(uInt8View.byteLength, uInt8View[11]);
};
复制代码
共享内存是另外一个使得主线程与web workers以前能够高效传递(共享)数据的方式。SharedArrayBuffer即是其实现。不过因为安全问题这一特性最终被关闭。
咱们能够经过worker-loader来在webpack中使用web worker。
import Worker from 'worker-loader!./worker.js';
const worker = new Worker();
worker.postMessage('start');
worker.onmessage = function(ev) {};
复制代码
随后咱们打包代码,在打包后的代码中咱们应该能够看到相似下面的代码:
module.exports = function() {
return new Worker(__webpack_require__.p + "61a1c1b143c4403de10b.worker.js");
};
复制代码
在这里能够看到worker-loader
使用new Worker(url)
的方式来加载web worker。
若是咱们将以上的代码修改成:
import Worker from 'worker-loader?inline=true&fallback=false!./worker.js';
复制代码
并从新打包。此次咱们将在vendor.js
(根据你的配置也多是其余文件名)中找到相似以下的代码:
module.exports = function (content, url) {
try {
try {
var blob;
try {
// BlobBuilder = Deprecated, but widely implemented
var BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder;
blob = new BlobBuilder();
blob.append(content);
blob = blob.getBlob();
} catch (e) {
// The proposed API
blob = new Blob([content]);
}
return new Worker(URL.createObjectURL(blob));
} catch (e) {
return new Worker('data:application/javascript,' + encodeURIComponent(content));
}
} catch (e) {
if (!url) {
throw Error('Inline worker is not supported');
}
return new Worker(url);
}
};
复制代码
这里worker-loader
会尝试经过new Blob
以及URL.createObjectURL
来将worker的代码内联,这里的content
就是相应worker的代码文本。固然若是失败仍是会回退为使用url
加载web worker。
Shared worket提供了一种跨窗口/iframe等通讯的途径。接下来咱们用chrome来运行一下demo。
// index.html
<script type="text/javascript">
const id = (+new Date()).toString(32);
const myWorker = new SharedWorker('./js/shared.js');
myWorker.port.start();
myWorker.port.onmessage = function(ev) {
const { type, id, data } = ev.data;
switch(type) {
case 'created':
console.log(data);
break;
}
};
myWorker.port.postMessage({
id,
type: 'start',
});
</script>
复制代码
在页面上咱们使用(+new Date()).toString(32)
来将当前时间戳转换而成的字符串做为id(这里咱们假设它的惟一性是有保障的)。
而后咱们经过new SharedWorker(filePath)
来加载shared worker,而且用myWorker.port.start()
启动它。这会触发shared worker的onconnect
。
接着咱们经过port.onmessage
设置消息接收回调,并使用port.postMessage
向其发出一条消息。
const ids = [];
const ports = [];
const broadcast = function (data) {
for (let port of ports) {
port.postMessage(data);
}
};
onconnect = function(e) {
const port = e.ports[0];
ports.push(port);
port.addEventListener('message', (ev) => {
const { type, id } = ev.data;
switch(type) {
case 'start':
ids.push(id);
broadcast({
id,
type: 'created',
data: ids,
});
break;
}
});
port.start();
};
复制代码
每当有shared worker接入,咱们都在onconnect
中将对应的port
保存下来,这样咱们就能实现广播机制。
咱们经过port.addEventListener('message', handler)
的方式添加接受消息的侦听,以后用port.start()
启动它。
当咱们收到start
消息后,咱们将新接入的信道的id
保存下来,并这一消息广播出来(发送给全部已连接的ports
)。
若是咱们前后在chrome
浏览器中打开多个tab
并打开咱们的页面,就能看到新信道接入的消息被广播了。经过这样的方式,咱们就能在多个窗口/iframe等之间实现通讯了。
今天介绍了web workers的基本使用,接下来要介绍一下如何动态的建立内联的web workers。