前端手中最重要的一把剑,就是JavaScript。
然而JavaScript是单线程运行的,在同一时间内只能执行一个任务。当JavaScript处理的程序越来越复杂的时候,需要处理的任务涉及大规模CPU运算,比如图像、视频的解析。单线程运行就会容易造成UI阻塞、页面卡顿这些问题。
所以浏览器、Node都引入了多线程模式。浏览器的多线程模式是HTML5规范,Node多线程是Node官方模块。
浏览器多线程——Web Worker
在浏览器中通过Web Worker使用多线程,在Web Worker线程中,直接运行JavaScript代码,但是不能操作DOM节点,也不能使用window对象的默认方法和属性、不能使用webSockets,IndexedDB这些数据存储机制。
首先在脚本里面使用new Worker语句创建一个Worker线程。然后主线程或者Worker线程中,可以通过postMessage方法向另一个线程发送消息,使用onmessage事件函数来接受另一个线程发送的消息。比如:
Worker线程是分为专用Worker线程和共享Worker线程,专用线程是是有一个父线程,共享线程可以有多个父线程。
专用Worker线程
现在有这样的一个代码,需要计算1到300000000000的总和,打开浏览器,在代码运行期间,浏览器会陷入卡顿,如下图所示:
这样时间久了,浏览器会崩溃的。
所以我们要把涉及到大量运算的任务放到Worker线程中执行,这样就不阻塞UI的渲染了,现在在a.js中创建代码,如下:
console.time("主线程占用时长");
let worker = new Worker("./b.js");
worker.onmessage = function (event) {
console.log('计算结果: ' + event.data);
}
let runTimes = 300000000;
worker.postMessage(runTimes);
console.timeEnd("主线程占用时长");
b.js代码为:
self.onmessage = function (event) {
console.time("Worker线程占用时长");
let result = 0;
for (let i = 0; i < event.data; i++) {
result += i;
}
self.postMessage(result);
console.timeEnd("Worker线程占用时长");
}
执行效果如下:
等到Worker线程的任务执行完成,会打印结果,如下图:
这样大大降低主线程的负担。
处理错误
在主线程里面监听Worker线程是否出错。相关的参数如下:
- message:错误消息
- filename:发生错误的脚本文件名
- lineono:错误在脚本中的行号
对上面的代码稍作调整:
let worker = new Worker("b.js");
worker.onmessage = function (event) {
console.log(`Result is "${event.data}"`);
};
worker.onerror = function (event) {
console.log("message: " + event.message);
console.log("filename: " + event.filename);
console.log("lineno: " + event.lineno);
}
worker.postMessage(null);
然后,b.js抛出一个错误:
self.onmessage = function (event) {
throw new Error("Something wrong!");
};
效果如下: