Web Worker?
我们都知道JavaScript是单线程的,至于为什么是单线程的,以下内容来源阮一峰前辈的文章
作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。
但这样的话,并不是很高效,因为单线程无法充分发挥计算机的计算能力。所以 为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。
Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。
在使用Web Worker前需要注意
- Worker 的全局对象是WorkerGlobalScope,与主线程不一样,,所以其无法读取主线程所在网页的 DOM 对象,也无法使用document、window、parent这些对象。但是,Worker 线程可以使用navigator对象和location对象。
- Worker 线程不能执行alert()方法和confirm()方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求。
Web Worker 基本使用
主线程
创建线程
var worker1 = new Worker('worker1.js');
基本操作
worker1.postMessage('Hello World');
worker1.onmessage=e=>{
console.log(e.data);
}
worker1.terminate();
Worker线程
self 代表子线程自身,即子线程的全局对象。
self.addEventListener('message', function (e) {
self.postMessage(e.data);
});
addEventListener('message', function (e) {
postMessage( e.data);
});
self.close()
close();
Web Worker的优势在哪?
我们通过一个例子来说明多线程的优势
在没有线程的情况下,主线程运行三个斐波那契函数所需事件如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
function fb(n){
if(n==1||n==2){
return 1;
}
return fb(n-1)+fb(n-2)
}
console.time('三个fb执行时间');
var result=fb(40)
var result=fb(40)
var result=fb(40)
console.timeEnd('三个fb执行时间')
</script>
</body>
</html>
现在我们创建三个进程,每一个进程运行一个函数
主线程代码
var worker1=new Worker('worker1.js');
var worker2=new Worker('worker2.js');
var worker3=new Worker('worker3.js');
worker1.onmessage=e=>{
console.log(e.data);
}
worker2.onmessage=e=>{
console.log(e.data);
}
worker3.onmessage=e=>{
console.log(e.data);
}
另外三个线程代码如下
function fb(n){
if(n==1||n==2){
return 1;
}
return fb(n-1)+fb(n-2)
}
console.time('fb执行时间1');
var result=fb(40)
console.timeEnd('fb执行时间1')
self.postMessage('worker1');
function fb(n){
if(n==1||n==2){
return 1;
}
return fb(n-1)+fb(n-2)
}
console.time('fb执行时间2');
var result=fb(40)
console.timeEnd('fb执行时间2')
self.postMessage('worker2');
function fb(n){
if(n==1||n==2){
return 1;
}
return fb(n-1)+fb(n-2)
}
console.time('fb执行时间3');
var result=fb(40)
console.timeEnd('fb执行时间3')
self.postMessage('worker3');
从这个例子我们可以看出,如果我们没有线程,主线程执行三个函数需要9.669s 的时间。而当我们创建三个线程,一个线程执行一个函数时,主线程得到结果的最长时间为2.217s 。
所以在实际中,我们一般使用Worker线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务可以交由 Worker 线程执行,主线程(通常负责 UI 交互)能够保持流畅,不会被阻塞或拖慢。
|