JavaScript是单线程的,并不是说它是单线程语言,只能说在浏览器中运行是单线程的,单线程会免去许多麻烦,比如说,有两个线程同时进行DOM操作,一个是在父级下添加子元素,一个是删除这个父级元素,这样会冲突的。但是呢?浏览器是多线程的,浏览器打开多个标签页依旧运行良好,为了充分利用,就有了 Web Worker ,它为 JavaScript 创造多线程环境,允许主线程创建子线程,将一些任务分配给子线程运行。在主线程运行的同时,子线程在后台运行,两者互不干扰。等到子线程完成计算任务,再把结果返回给主线程。但这并不是说,JavaScript就不是单线程了,要知道,子线程是主线程控制的。
单线程证明
案例:
<!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>Worker多线程</title>
</head>
<body>
<button onclick="start()">开始</button>
<button onclick="stop()">停止</button>
<script type="text/javascript">
function start(){
let num = 0
setInterval(() => {
num++
console.log(num)
}, 500);
}
function stop(){
alert('只要不点击暂停,这个alert任务就没有结束')
}
</script>
</body>
</html>
结果:
每0.5秒在队列中添加一个任务,点击停止时,添加了一个alert任务,定时器这个不断添加的任务就得排队等待不再继续累加。
Web Worker的使用
1.主线程创建worker。
var worker = new Worker('work.js');
2.主线程调用worker.postMessage()方法,向 Worker 发送内容,可以理解为参数。
worker.postMessage( 'a' );
worker.postMessage([1,2,3,4,5]);
worker.postMessage({
name:'zhangsan',
age:20
});
3.子线程通过message来监听主线程传递的信息
this.addEventListener('message', function (e) {
this.postMessage('e' + e.data); //传递过来的信息
}, false);
//或者
onmessage = function(e){
console.log('e',e.data); //传递过来的信息
}
4.主线程接收子线程返回的数据
worker.onmessage = function(e){
console.log('e',e)
}
//或者
worker.addEventListener('message', function (e) {
console.log('e',e.data)
}, false);
5.关闭线程,节约资源
self.close()
worker.terminate()
(详情见下边案例)
<body>
<button onclick="start()">开始</button>
<button onclick="stop()">停止</button>
<script type="text/javascript">
function start(){
var worker = new Worker('worker.js');
worker.postMessage(0);//将复杂计算交给子线程,可以理解为给参数让子线程去操作。
worker.onmessage = function(e){
console.log('主线程打印',e.data);//接收子线程返回来的数据,这个打印是主线程打印。
}
}
function stop(){
alert('只要不点击暂停,这个任务就没有结束,定时器每0.5秒添加的任务需要排队等待。')
}
</script>
</body>
worker.js文件
// 响应主线程发过来的数据进行处理
onmessage = function(e){
// console.log('e',e.data)
var num = e.data;//接收从主线程传过来的值
setInterval(() => {
num++
console.log(num)
postMessage(num);//把计算结果传回给主线程
}, 500);
}
结果:
主线程中走alert任务的时候,主线程打印已经停止,子线程继续再执行,不受影响。当alert任务点击确定结束,堆积的打印任务一口气都“吐”了出来。
关闭worker线程的方式
1.worker线程(子线程)自己关闭:self.close();
2.主线程控制关闭:worker.terminate();
案例:-----worker.terminate()
<body>
<button onclick="start()">开始</button>
<script type="text/javascript">
function start(){
var worker = new Worker('worker.js');
worker.postMessage(0);
worker.onmessage = function(e){
if(e.data == 5){
worker.terminate();
console.log('主线程打印:worker线程结束')
}
}
}
</script>
</body>
结果:
案例: -----self.close()
worker.js
onmessage = function(e){
var num = e.data;//接收从主线程传过来的值
setInterval(() => {
num++
console.log(num)
if(num == 5){
self.close()
}
postMessage(num);//把计算结果传回给主线程
}, 500);
}
结束。