概述
Js是单线程的,也就是任务得一件件的按照顺序处理,前面的任务没有处理完成,后面的只能等着。但是随着科技的发展,现在的计算机都是多核的CPU,单线程无法发挥计算机的能力。
Web Worker 的作用就是在js主线程上创建一个辅助线程(Worker线程),将一些任务分配给Worker线程运行。两个线程互不干扰,也能相互通信。用户在主线程上进行UI交互时,不会卡顿和阻塞。Worker 线程也不会因为主线程交互而被打断。这个和之前的Service Worker很类似
注意,Worker 线程比较耗费资源,不应该过度使用,任务完成后要关闭。同时,Web Worker 有以下几个使用注意点:
-
同源限制,执行Worker线程的脚步文件必须和主线程在一个host下
-
DOM限制,Worker线程不能操作document,window,parent等对象,但可以操作navigator和location对象
-
脚本限制,Worker 线程不能执行aler()和confirm()方法,但可以使用XMLHttpRequest 对象发出 AJAX 请求。
-
引用路径,Worker 线程引用文件时,必须是同源下的相对或者绝对路径。
-
上下文环境,Worker线程和主线程不在同一个上下文环境,他们之间通过xx.postMessage相互通信。
-
通信数据独立,主线程和Worker线程传递的数据都是独立的,通信的数据都是先转字符串再还原的过程,所以不用担心对象深拷贝的问题
用法
主线程
1、在主线程上新建worker线程
var worker = new Worker('./work.js');
构造函数Worker引用的脚步文件work.js就是Worker线程的执行任务。如果构造函数Worker引用的参数路径不对,Worker会默认失败,Worker线程将不执行。
2、主线程给Woker线程发消息
worker.postMessage('Hello World');
worker.postMessage({method: 'echo', args: ['Work']});
主线程通过实例对象worker的postMessage向Worker发消息。postMessage的参数可以是任意类型。
3、主线程监听Woker线程发消息
worker.onmessage = function (event) {
console.log('Received message ' + event.data);
doSomething();
}
function doSomething() {
// 执行任务
worker.postMessage('Work done!');
}
主线程通过调用实例onmessage方法监听Worker线程发来的消息(event.data)
4、主线程关闭Woker线程
worker.terminate();
Worker线程
1、Worker线程监听主线程消息
self.addEventListener('message', function (e) {
self.postMessage('You said: ' + e.data);
}, false);
self代表Woker线程的全局对象,等同下面两种写法:
// 写法一
this.addEventListener('message', function (e) {
this.postMessage('You said: ' + e.data);
}, false);
// 写法二
addEventListener('message', function (e) {
postMessage('You said: ' + e.data);
}, false);
2、Worker线程给主线程发送消息
self.addEventListener('message', function (e) {
var data = e.data;
switch (data.cmd) {
case 'start':
self.postMessage('WORKER STARTED: ' + data.msg);
break;
case 'stop':
self.postMessage('WORKER STOPPED: ' + data.msg);
self.close(); // Terminates the worker.
break;
default:
self.postMessage('Unknown command: ' + data.msg);
};
}, false);
和主线程方法一样,通过调用Worker线程的postMessage向主线程发送消息,参数就是发送的数据
3、Worker线程关闭自身
self.close();
拓展
1、可以在同一个文件里编写主线程和worker线程。
<!DOCTYPE html>
<body>
//worker线程的脚本
<script id="worker" type="app/worker">
addEventListener('message', function () {
postMessage('some message');
}, false);
</script>
//主线程脚本
<script>
var blob = new Blob([document.querySelector('#worker').textContent]);
var url = window.URL.createObjectURL(blob);
var worker = new Worker(url);
worker.onmessage = function (e) {
// e.data === 'some message'
};
</script>
</body>
</html>
上面代码中,先将嵌入网页的脚本代码,转成一个二进制对象,然后为这个二进制对象生成 URL,再让 Worker 加载这个 URL。这样就做到了,主线程和 Worker 的代码都在同一个网页上面。
API
Worker()
var myWorker = new Worker(jsUrl, options);
主线程全局对象提供了Worker构造函数,它接收两个参数,第一个参数是脚本文件的的同源网址,该参数必须的,否则会报错;第二个参数是可选的配置对象,它的主要作用是指定Worke线程的名称,用来区分多个Worker线程。
// 主线程
var myWorker = new Worker('worker.js', { name : 'myWorker' });
// Worker 线程
self.name // myWorker
Worker()创建的实例对象,提供了以下属性和方法:
-
Worker.onerror:指定 error 事件的监听函数。
-
Worker.onmessage:指定 message 事件的监听函数,发送过来的数据在Event.data属性中。
-
Worker.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。
-
Worker.postMessage():向 Worker 线程发送消息。
-
Worker.terminate():立即终止 Worker 线程。
Web Worker 有自己的全局对象,不是主线程的window,而是一个专门为 Worker 定制的全局对象。因此定义在window上面的对象和方法不是全部都可以使用。
Worker 线程有一些自己的全局属性和方法。
-
self.name: Worker 的名字。该属性只读,由构造函数指定。
-
self.onmessage:指定message事件的监听函数。
-
self.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。
-
self.close():关闭 Worker 线程。
-
self.postMessage():向产生这个 Worker 线程发送消息。
-
self.importScripts():加载 JS 脚本。
兼容性
文章和自己个人网站同步: