前言

做了这么久的前端,在印象中一直以来认为js只是单线程的,包括一些异步的操作也只能说算是伪多线程。
现在可以很确定的告诉你,js并不是只能单线程,是可以多线程的。
它主要通过HTML5一个新的API - Worker来实现的。
接下来我们就进一步来了解如何一步一步的实现一个简单的多线程。

正文

首先我们来看一下它的兼容性:

整体来说兼容性不错,只有IE只兼容到10略微有些差强人意。
那么我们要如何来使用它呢?
首先要通过Worker()构造函数创建支线程对象,该函数接受一个指定URL的js文件。
即为:

Worker(
  in DOMString aStringURL
);

该对象会返回一个worker对象,该对象即为支线程对象。
到这里,我们要注意:

  1. 要清楚主线程和支线程的概念,在当前环境中运行的js即为主线程,通过Worker()创建的线程即为支线程;
  2. 主线程和支线程必须遵循同源策略
  3. 支线程并不能进行DOM操作,它是浏览器中后台独立出的运行,但是还是有一些方法是可以调用的,这里我们给出一个列表:函数列表

在创建好Worker对象后,它主要提供有两个方法:

//用于主线程和支线程之间相互通信
void postMessage(in JSObject message);
//用于关闭支线程
void terminate();

其中postMessage()方法在主线程和支线程环境中通用,主线程中通过创建的Worker对象调用该方法并传入一个通信对象,便将数据传输给支线程。
同理,在支线程中也可以回传通信对象给主线程,但是在支线程环境中我们通过什么对象来调用这个方法呢?
此时,在支线程环境中,有一个默认的self对象,可以通过它来传输通信对象,即为:

self.postMessage({ number: 1 });

至于terminate()就是简单的关闭支线程,只能在主线程环境中使用:

worker.terminate();

我们刚刚只提到了主支线程之间互相如何发送通信对象,但是并未提到如何接收,接下来我们来看它们又是如何如想接收信息的。
该对象还提供两个事件:

//主支实时接收通信的事件,其中我们传输的通信对象保存在回调函数参数e.data对象上
Worker.onmessage = function(e) { ... };
//用于出现错误时的回调
Worker.onerror = function() { ... };

其中onmessage事件也是主支线程都可以使用的,原理同postMessage()方法,在主线程通过Worker对象调用,在支线程通过self对象调用。
它可以做到实时监听两个线程之间的postMessage()方法,当其中一方发起传输,另一方就进入事件并接收到通信数据对象。
具体的使用我们就讲到这里,接下来我们看一个Worker的具体使用。
我们知道在单线程的环境下,通过alert()的方式弹窗可以阻塞线程的运行,那么接下来我们看:
在传统环境下:

var i = 0;
setInterval(function(){
    i++;
    if(i === 10){
        alert('yep');
    }
},1000);

在我们不点击弹窗关闭的情况下,计数会一直停留在10上。

Worker环境下:

//main.js
var worker = new Worker('worker.js');
worker.onmessage = function(e){
    if(e.data === 10){
        alert('yep');
    }
};
//worker.js
var i = 0;
setInterval(function(){
    i++;
    self.postMessage(i);
},1000);

在当前环境下,我们仍旧不点击弹窗关闭,计数并不会停止,而是继续计数,并未受到主线程的阻塞影响。

最后

关于多线程的内容就告一段落了。
至于它的用途,可以使用在复杂的运算、在考虑到线程阻塞的情况下可以使用。
我们将在下一篇文章中来具体使用它到实际当中。