HTML5 worker工作线程

31 Aug

JavaScript原本是单线程的,一次只能处理一件事。如果处理数组占据大量时间,用户点击可能就没反应了,或页面刷新很慢,影响用户体验。HTML5提供了工作线程worker thread。可以新建一个工作线程来处理数组,确保之后的事情能顺利完成。

工作线程

定义在一个单独的JavaScript文件中。要让工作线程工作,浏览器会发送一个消息,工作线程收到消息开始工作。完成工作后发回消息给浏览器。

1.HTML中加载的js相当于主线程:

//可以将HTML中加载的myThread.js理解为主线程
<script src="js/myThread.js"></script>

2.定义个worker.js,内容可以暂时为空。(为定义工作线程做准备)

3.主线程中定义工作线程,发送消息让工作线程开始工作,定义onmessage和onerror回调函数,分别处理工作线程成功或失败完成工作的事件

window.onload = function() {
    //将第2步新建的空的worker.js定义成工作线程(※1)
    var worker = new Worker("js/worker.js");

    //主线程向工作线程发送消息(※2)
    worker.postMessage("startwork");

    //工作线程成功完成工作后的回调函数(※3)
    worker.onmessage = function(event) {
        document.getElementById("output").innerHTML = event.data;
    }
 
    //工作线程完成工作失败后的回调函数(※3)
    worker.onerror = function(error) {
        document.getElementById("output").innerHTML =
            "There was an error in " + error.filename +
            " at line number " + error.lineno +
            ": " + error.message;
    };
}

※1:只能用一个js文件创建工作线程,而不能用函数。因为规定工作线程不能访问DOM,如果向Worker构造函数传入一个函数,该函数可能包含DOM或主JavaScript代码的引用,就违反规则。因此工作线程的设计者选择的做法是只能传递一个js文件的URL

※2:消息可以是一个简单的字符串,也可以更复杂如worker.postMessage([1,2,3,4]);数组,worker.postMessage({“message”:“ping”, “count”: 5});JSON对象。但不能worker.postMessage(func);发送函数,理由见※1。

※3:回调函数的参数Event对象:我们只对data和target属性感兴趣,data属性包含工作线程发送的消息,target属性是发出这个消息的工作线程的引用,方便我们知道来自哪个工作线程

4.实现worker.js

onmessage = function(event) {
    //根据主线程发来的消息,做相应的处理
    if (event.data == "startwork") {
        ......    //此处省略行业务相关代码

        //工作完成,回发消息通知主线程,主线程的onmessage方法将被触发
        postMessage("done!");
    } else {
        postMessage("Are you sleepwalking now?");
    }
}

工作线程注意点

1.上面的※1已经说明过了,这里再啰嗦一遍:工作线程无法访问浏览器代码能够访问的很多运行时的对象,如DOM或主代码中的所有变量和函数。但可以访问localStorage或做出XMLHttpRequest请求。之所以如此设计,是因为必须保证只有一个线程能访问DOM,否则多个线程并发修改DOM会很容易导致DOM处于一种不一致的状态。想象一下线程A为某个DOM元素增加子DOM元素,线程B删除该DOM元素。而线程之间无法保证运行顺序,如果线程B先完成,线程A取DOM元素时将会出错。现在明白工作线程和传统意义上的多线程的区别了吧,工作线程相当于一个二等公民。

2.主线程向工作线程发送的消息对象,不会成两者间的共享对象。工作线程会得到对象的副本,工作线程中对副本的修改不会影响到主线程中该对象,工作线程发出的对象也是如此,主线程只能得到该对象的副本

何时使用工作线程

学习了工作线程的基本原理和技术后,请不要滥用它。因为工作线程的负荷是很重的,通常数据量不大时压根没必要使用工作线程。

但在页面做图像处理时就非常有用了,不用工作线程的话,加载显示Mandelbrot分形图页面的速度将难以忍受:

Mandelbrot

Leave a Reply

Your email address will not be published. Required fields are marked *