众所周知,Js是单线程运行的,也就是运行完前一个函数之后,才能运行后一个函数。犹如单行道的车辆,其实这是js的一个短板,同时也导致了运行阻塞。虽然有时候我们可以通过定时器来并向执行代码,但这并不能真正解决问题。

在html5中有一个名为Web worker的api,它的产生就犹如人们发明了立交桥,使得js支持多线程运行。假设当前页面的代码为主线程,而创建Web worker则是为主线程开辟了另外一个新的子线程,因为是并行运行的,所以这个子线程不会阻塞主线程的执行。

Web worker是运行在后台的js,可以让web应用程序具备后台处理能力,即我们可以把一些非常复杂的运算、或一些很耗性能的js代码交给Web worker这个子线程来处理,然后再将得到的结果返回到主线程中,从而加快了页面的响应,但是正因为它是后台运行的js,所有它不能读取dom对象,但可以执行setInterval、setTimeout以及ajax请求。

相关用法

使用之前浏览器的兼容处理,然后需要新建Worker方法,这个方法的参数是后台js文件:

if (window.Worker) {
  // 支持web worker
  var webWorker = new Worker("web-worker.js");
} else {
  alert("您的浏览器不支持web worker !");
}

那么问题来了,如何将主线程的数据处理任务交给子线程呢?也就是说它们之间如何通信?Web worker提供了postMessage函数和 onmessage事件,postMessage可以接收一个字符串或者json对象作为它的参数,在主线程中据说是开启webWorker,来看如下代码:

主线程(主页面):

webWorker.postMessage(str);
webWorker.addEventListener("message",function(e){
  console.log(e.data);
},false)

子线程(web-worker.js):

self.addEventListener("message",function(e){
//对e.data进行非dom计算操作
   var str = e.data;
  self.postMessage(str); // 返回给主线程
});

这里的self就是子线程,它通过使用onmessage事件监听变化,使用postMessage将计算的结果返回到主线程。如果你不想把数据处理的函数放在Web worker.js中,你也可以将其单独存为一个js文件,然后再在Web worker引入这个js文件,它也支持引入多个js文件,运行效果是一致。如下:

importScripts("fn1.js","fn2.js");

正如前面所说,计算结果返回到主线程后,那么如何接收它呢?我们同样可以通过在web Worker上绑定onmessage事件,得到计算结果即 e.data :

webWorker.addEventListener("message",function(e){
  alert(e.data);
},false)

当程序运行结束后,我们需要关闭web worker,而关闭子线程主要可以通过以下两种方式;

在主线程关闭web worker:

webWorker.terminate();

或者在子线程关闭web worker:

self.close();

一些限制

子线程加载的js文件,必须与主线程在同一个域。也就是Web worker不能引入跨域的js文件。

因为Web worker是运行在后台的js程序,所以它不能像正常的js一样可以进行变量和函数的读取,也不能对dom进行操作。

最后Web worker必须是在服务器环境下运行。

最后还是来看一个列子:正常情况与使用web worker 生成30000个div所需时间,页面中显示了运行前后时间差,你也可以F12打开控制台查看更精确的运行时间。