Jacleklm's Blog

Web Worker

2019/11/22

概念

解决JS单线程,遇到计算密集型或高延迟的任务时发生阻塞的问题
Web Worker 的作用,就是允许主线程创建 Worker 线程,承担一些费时的任务
Worker线程可以有多个,但是使用完最好计时关闭

一个简单的例子帮助理解

改进前:

1
2
3
4
5
6
7
8
9
10
11
12
<body>
<button id="btn">点击</button>
<h1 id="num"></h1>
<script>
//在子线程 模拟一个耗时操作
for (var i = 0; i < 1000000000; i++) { }
num.innerHTML = i;
box.onclick = function () {
alert("耗时操作导致页面阻塞,打开慢");
}
</script>
</body>

主线程中存在耗时任务,发生阻塞
改进后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<button id="btn">点击</button>
<button id="stop">停止子线程</button>
<h1 id="num"></h1>
<script>
// 创建子线程。把耗时操作放在子线程的文件中,Worker不能读取本地文件,脚本【必须来自网络,并且同源】
var webWorker = new Worker('./worker.js')
webWorker.onmessage = function (e) {
num.innerHTML = e.data
}
btn.onclick = function () {
alert("有子线程后主线程快得多了");
}
stop.onclick = function () {
// 【终止Web Worker】
webworker.terminate();
}
</script>
</body>

子线程

1
2
3
4
5
6
(function  () {
//模拟一个耗时操作
for (var i = 0; i < 1000000000; i++) { }
//耗时操作完毕后,调用postMessage方法回到主线程,并且把数据传回去
postMessage(i);
})();

在主线程创建web worker子线程,让子线程承担费时任务,主线程不会阻塞

Web Worker的限制

  • new 传递的脚本文件必须来源网络并同源
  • 无法访问DOM节点;
  • 无法调用alert()或者confirm之类的函数;
  • 无法访问window、document之类的浏览器全局变量;但是可以用navigator对象和location对象

不过Web Worker中的Javascript依然可以使用setTimeout(),setInterval()之类的函数,也可以使用XMLHttpRequest对象来做Ajax通信

使用方法

主线程

1
2
3
4
var worker = new Worker('work.js') // 用new创建子线程,参数是一个同源网络的脚本文件
worker.postMessage('Hello World'); // 主线程向 Worker 发消息
worker.onmessage = function(ev) { console.log(ev.data) } // 主线程通过监听message事件,接收子线程发回来的消息(事件的data属性)
worker.terminate() // 关闭 Worker

worker线程

1
2
3
addEventListener('message', function (evt) { console.log(ev.data) } // 同理通过监听message事件,接收主线程发回来的消息(事件的data属性)
self.postMessage('Hello World') /// 向主线程发送消息
self.close() // 关闭 Worker

使用XMLHttpRequest与服务端通信

在子线程文件中写:

1
2
3
4
5
6
7
8
addEventListener("message", function(evt){  
var xhr = new XMLHttpRequest();
xhr.open("GET", "某个服务器的url");
xhr.onload = function(){
postMessage(xhr.responseText);
};
xhr.send();
},false);

上面的代码向服务器发出GET请求,并注册了获取数据后的onload事件。结果是获得数据后把数据传给主线程

通过Error事件捕捉错误信息

Web Worker恰恰提供了error事件,供开发者捕捉错误信息

1
2
3
4
5
6
var worker = new Worker("scripts/lengthytask.js");  
worker.addEventListener("error", function(evt){
alert("Line #" + evt.lineno + " - " + evt.message + " in " + evt.filename);
}, false);
worker.postMessage(10000);
});

如上可见, Worker对象可以绑定error事件;而且evt对象中包含错误所在的代码文件(evt.filename)、错误所在的代码行数(evt.lineno)、以及错误信息

参考资料
阮一峰-Web Worker 使用教程
关于Web Worker你必须知道的7件事

CATALOG
  1. 1. 概念
  2. 2. 一个简单的例子帮助理解
  3. 3. Web Worker的限制
  4. 4. 使用方法
    1. 4.1. 主线程
    2. 4.2. worker线程
    3. 4.3. 使用XMLHttpRequest与服务端通信
    4. 4.4. 通过Error事件捕捉错误信息