1. 什么是webworker
理论多代码少的一个新特性
MDN是这样说的
Web Worker为Web内容在后台线程中运行脚本提供了一种简单的方法。线程可以执行任务而不干扰用户界面
一个worker是使用一个构造函数创建的一个对象(e.g. Worker()) 运行一个命名的JavaScript文件
这个文件包含将在工作线程中运行的代码;
workers 运行在另一个全局上下文中,不同于当前的window
因此,使用 window快捷方式获取当前全局的范围 (而不是self) 在一个 Worker 内将返回错误
可以这样理解:
- 创建Worker时,JS引擎向浏览器申请开一个子线程(子线程是浏览器开的,完全受主线程控制,而且不能操作DOM)
- JS引擎线程与worker线程间通过特定的方式通信(postMessage API,需要通过序列化对象来与线程交互特定的数据)
如果有非常耗时的cpu密集型工作,可以单独开一个Worker线程,因为里面不管如何翻天覆地都不会影响JS引擎主线程,
只待计算出结果后,将结果通信给主线程即可
而且注意下,JS引擎是单线程的,这一点的本质仍然未改变,Worker可以理解是浏览器给JS引擎开的外挂,专门用来解决那些大量计算问题。
2. webworker帮助我们解决了什么问题,该如何去使用webworker呢
一些非常耗时的工作会阻塞js的代码,这种情况就需要使用这个东西。
非常耗时的cpu密集型工作!!!
举个例子:我们在两个button中间加一段耗时的script代码。
<body>
<button>111</button>
<script src="./1.js"></script>
<button>222</button>
</body>
1.js的耗时代码:
var start = new Date().getTime()
do {
var end = new Date().getTime();
} while (end - start < 3000)
console.log("5秒执行结束");
这里我们一般不这样写代码,但是确实一个很好的线程阻塞的问题,因为js的单线程,浏览器的渲染引擎将第一个button渲染结束的时候,执行了script脚本,cpu被js引擎占用,如果这个耗时操作不结束,渲染引擎将无法继续工作,导致页面不能正常的工作。
A. 开启worker
我们这个时候开启一个Worker即可解决这个问题。(注意这个不能读取本地的js文件,否则会报错,只能是服务端的js脚本文件)
<body>
<button>111</button>
<script>
// 创建新的线程执行05.js
var worker= new Worker("./1.js")
</script>
<button>222</button>
</body>
只是执行代码还是有些单薄的,我们还需要实现主线程与worker线程之间的通讯
B. 主线程 -> worker线程
主线程调用worker.postMessage()方法,向 Worker 发消息。
worker.postMessage('Hello World');
worker.postMessage({method: 'echo', args: ['Work']});
主线程通过worker.onmessage指定监听函数,接收子线程发回来的消息。
worker.onmessage = function (event) {
console.log('Received message ' + event.data);
doSomething();
}
function doSomething() {
// 执行任务
worker.postMessage('Work done!');
}
Worker 完成任务以后,主线程就可以把它关掉。
worker.terminate();
填坑举例:这个监听一般都是异步的所以我们在主线程关闭的时候要在onmessage中将数据处理完成之后关闭。
// 创建新的线程执行05.js
var worker = new Worker("./1.js")
// 向worker发数据
worker.postMessage("123")
// 接收worker的数据
worker.onmessage = function(event) {
console.log("主Received message:" + event.data);
wworkerterminate();
}
// w.terminate(); // 消息无法正常的传递,我们在传递前就关掉了
C. worker线程 -> 主线程
Worker 线程内部需要有一个监听函数,监听message事件,监听从主线程来的消息
self.addEventListener('message', function (e) {
self.postMessage('You said: ' + e.data);
}, false);
上面代码中,self代表子线程自身,即子线程的全局对象。因此,等同于下面两种写法。
// 写法一
this.addEventListener('message', function (e) {
this.postMessage('You said: ' + e.data);
}, false);
// 写法二
addEventListener('message', function (e) {
postMessage('You said: ' + e.data);
}, false);
子线程调用worker.postMessage()方法,向 主线程发消息。
postMessage("456")
子线程接收主线程的消息,并且发送给主线程结果,然后关闭自己
onmessage = function(event) {
console.log("新Received message:" + event.data);
postMessage("456")
self.close()
}
子线程关闭自己
self.close();
D. 关闭 Worker
使用完毕,为了节省系统资源,必须关闭 Worker。
// 主线程
worker.terminate();
// Worker 线程
self.close();
以上就已经完成了worker最基本的使用了。
还在阮一峰老师的博客上看到其他的两个方法在这里搬运一下
1. 在 Worker 内加载脚本
Worker 内部如果要加载其他脚本,有一个专门的方法importScripts()。
importScripts('script1.js');
该方法可以同时加载多个脚本。
importScripts('script1.js', 'script2.js');
2. 错误处理
主线程可以监听 Worker 是否发生错误。如果发生错误,Worker 会触发主线程的error事件。
worker.onerror(function (e) {
console.log([
'ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message
].join(''));
});
// 或者
worker.addEventListener('error', function (e) {
// ...
});
到这里,最基本的webworker的使用就结束了,希望大家能有所收获。
参考文档:
进程线程:https://segmentfault.com/a/1190000012925872
阮一峰:https://www.ruanyifeng.com/blog/2018/07/web-worker.html