Web Worker是什么?
- 要理解 Web Worker 首先要知道JavaScript语言是单线程的。
- 单线程:所有任务都在一个线程上完成,一次只能做一件事,一件事没做完,后面的任务都得排队,好比JavaScript是旅游景点的公共厕所,只有一个坑位,不论男女都得一个一个排着队等里面人出来了下一个才能上厕所。
- Web Worker:旅游景点一看这不行,我得新建一个厕所,缓解用户的如厕排队时间,这个新建的厕所就是Web Worker
作用
- Web Worker是为JavaScript创建了一个多线程环境,在主线程创造了工作线程,将一些任务分配给工作线程运行,好处是一些占用性能、时间较高的任务被工作线程负担,主线程就会流畅(主线程一般处理UI交互)。
副作用
- Web Worker线程一但被创建就会一直运行,不会被主线程的任务所打断,这也导致工作线程是比较耗费资源的,所以一但用完就要立即关闭。
需要注意的地方
- Web Worker是同源的,所以分配给它运行的任务、文件也必须和主线程同源。
- Worker 所在的全局对象和主线程的不一样,这也导致了它无法读取主线程所在网页的DOM对象,无法使用document、window、parent这些对象。但 worker 线程可以使用 navigator对象和location对象
- Worker 线程和主线程上下文环境不同,导致他们不能直接通信,必须要通过消息完成通信
- Worker 线程不能执行 alert() 方法 和 confirm() 方法,但可以使用XMLHttprequest对象发出AJAX请求。
基本用法
主线程
- 兼容
- 假如需要在项目中使用Worker,但又要考虑兼容问题那么谨慎起见,把代码写在if里是一个好的选择
if(window.Worker){
}
- 构造
- 主线程通过采用 new 关键字调用 Worker 的构造函数,新建一个Worker线程
- 注意:Worker() 构造函数的参数是一个脚本文件,由于Worker不能读取本地文件,所以这个脚本必须来自网络,如果没有下载成功,Worker会默默失败。
const worker = new Worker('work.js')
- 通信
- 主线程与worker线程进行通信需要在主线程调用postMessage()方法,向Worker发消息
- postMessage()方法的参数就是主线程传给Worker的数据。可以是各种数据类型,包括二进制数据
worker.postMessage('你好')
worker.postMessage({method: 'echo', args: ['Work'])
- 接着主线程需要通过 worker.onmessage() 指定监听函数接收子线程发回来的消息
- 监听函数参数的data属性可以获取 Worker 发来的数据。
worker.onmessage = function (event) {
console.log('Received message ' + event.data);
doSomething();
}
function doSomething() {
worker.postMessage('Work done!');
}
- 当Worker 完成任务后,主线程就可以关掉它,节约性能
worker.terminate()
Worker 线程
- 通信
- Worker线程内需要一个监听函数,监听 message 事件
- self代表worker线程自身,也就是worker线程的全局对象
worker.onmessage = function (event) {
console.log('Received message ' + event.data);
doSomething();
}
function doSomething() {
worker.postMessage('Work done!');
}
this.addEventListener('message', function (e) {
this.postMessage('You said: ' + e.data);
}, false);
addEventListener('message', function (e) {
postMessage('You said: ' + e.data);
}, false);
- 使用self.onmessage指定监听函数。监听函数的参数是一个时间对象,它的data属性包含主线程发来的数据。用self.postMessage() 方法向主线程发送消息。
- 可以根据主线程发来的不同数据,让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();
break;
default:
self.postMessage('Unknown command: ' + data.msg);
};
}, false);
- 关闭线程
- 在worker线程中,可以调用其自身的close()方法进行关闭
close()
- 加载脚本
- 在worker内部如果需要加载其他脚本,可以使用importScripts()方法
importScripts('script1.js');
importScripts('script1.js', 'script2.js');
错误处理
- 主线程监听Worker错误,如果发生错误,Worker会触发主线程的error事件
worker.onerror(function (event) {
console.log([
'ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message
].join(''));
});
- Worker内部监听error事件
worker.addEventListener('error', function (event) {
});
使用场景
- 在很耗费主线程性能的程序里,就可以考虑使用Web Worker,防止页面卡死带来的用户体验问题
- 加密:项目中如果有前端数据加密的操作,也能放在Worker线程进行,让其完成工作的纯算法
- 预加载:优化网站或web应用改进数据加载时间,利用Web Workers提前加载和存储数据。