• 浅谈webWorker


    一、webWorker之初体验

    "setTimeout那些事儿"中,说到JavaScript是单线程。也就是同一时间只能做同一事情。

    也好理解,作为浏览器脚本语言,如果JavaScript不是单线程,那么就有点棘手了。比如,与用户交互或者对DOM进行操作时,在一个线程上修改某个DOM,另外的线程删除DOM,这时浏览器该如何抉择呢?

    所以,JavaScript是单线程也是有背景的。

    如下:

    <!DOCTYPE html>
        <head>
            <title>singleThread</title>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        </head>
        <body>
            <script>
                //添加到任务队列中,待同步任务所处的‘执行栈’执行完毕,1秒后执行任务队列中的这个匿名函数
                setTimeout(function(){
                    console.log('come on');
                },1000);
                //只要不关闭该alert,‘执行栈’就没结束,从而也就不会进入到任务队列中
                alert('waiting');
            </script>
        </body>
    </html>

    但,HTML5引入了一个工作线程(webWorker)的概念。它允许开发人员编写能够长时间运行而不被用户所中断的后台程序,去执行事务或者逻辑,并同时保证页面对用户的响应。

    简而言之,就是允许JavaScript创建多个线程,但是子线程完全受主线程控制,且不得操作DOM

    从而,可以用webWorker来处理一些比较耗时的计算。

    如下,主页面:

    <!DOCTYPE html>
        <head>
            <title>worker</title>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
            <script>
                function init(){
                    //创建一个Worker对象,并向它传递将在新线程中执行的脚本url
                    var worker = new Worker('worker.js');
                    //接收worker传递过来的数据
                    worker.onmessage = function(event){
                        document.getElementById('result').innerHTML+=event.data+"<br/>" ;
                    };
                };
            </script>
        </head>
        <body onload = "init()">
            <div id="result"></div>
        </body>
    </html>

    下面是worker.js的内容:

    var i = 0;
    function timedCount(){
        for(var j = 0, sum = 0; j < 100; j++){
            for(var i = 0; i < 100000000; i++){
                sum+=i;
            };
        };
        //将得到的sum发送回主线程
        postMessage(sum);
    };
    //将执行timedCount前的时间,通过postMessage发送回主线程
    postMessage('Before computing, '+new Date());
    timedCount();
    //结束timedCount后,将结束时间发送回主线程
    postMessage('After computing, ' +new Date());

    上面代码执行的流程是:创建的worker对象,并用onmessage方法接收worker.js里面postMessage传递过来的数据(event.data),并将数据追加到div#result中。

    所以,执行上面的代码结果如下:

     

                                                       图一

    待worker.js中的timedCount方法运算完后,执行postMessage操作,向主线程传数据,得图二。期间,并不影响主线程的运作。

                                                       图二

    二、webWorker之常用API

    接下来,再来看看关于worker的常用API:

    1、postMessage(data)

    子线程与主线程之间互相通信使用方法,传递的data为任意值。

    //worker = new Worker('url');
    //worker.postMessage传递给子线程数据,对象
    worker.postMessage({first:1,second:2});
    
    //子线程中也可以使用postMessage,如传递字符串
    postMessage(‘test’);

    2、terminate()

    主线程中终止worker,此后无法再利用其进行消息传递。注意:一旦terminate后,无法重新启用,只能另外创建。

    //worker = new Worker('url');
    worker.terminate();

    如,主页面:

    <!DOCTYPE html>
        <head>
            <title>worker</title>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
            <script>
                function init(){
                    var worker = new Worker('worker.js');
                    //每隔100毫秒,向子线程传递{name: 'monkey'}信息
                    setInterval(function(){
                        worker.postMessage({name: 'monkey'});
                    },100);
                    //当主线程worker收到来自子线程的消息后,触发message事件
                    worker.onmessage = function(event){
                        document.getElementById('result').innerHTML+=event.data+"<br/>" ;
                        //主线程使用terminate方法中断与子线程来往,在浏览器中只能显示一次event.data
                        worker.terminate();
                    };
                };
            </script>
        </head>
        <body onload = "init()">
            <div id="result"></div>
        </body>
    </html>

    子线程worker.js代码:

    //当主线程发来信息后,触发该message事件
    onmessage = function(event){
        //向主线程发送event.data.name信息
        postMessage(event.data.name);
    };

    3、message

    当有消息发送时,触发该事件。且,消息发送是双向的,消息内容可通过data来获取。

    message使用,可见terminate中的demo

    4、error

    出错处理。且错误消息可以通过e.message来获取。

    如下:

    //worker = new Worker('url');
    worker.onerror = function(e){
        //打印出错消息
        console.log(e.message);
        //中断与子线程的联系
        worker.terminate();
    }

    另:worker线程从上到下同步运行它的代码,然后进入异步阶段来对事件及计时器响应,如果worker注册了message事件处理程序,只要其有可能触发,worker就一直在内存中,不会退出,所以通信完毕后得手动在主线程中terminate或者子线程中close掉但如果worker没有监听消息,那么当所有任务执行完毕(包括计数器)后,他就会退出。

    三、worker上下文

    先看下面这段代码:

    主页面:

    <!DOCTYPE html>
        <head>
            <title>worker</title>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
            <script>
                function init(){
                    var worker = new Worker('worker.js');
                    //接收消息事件
                    worker.onmessage = function(event){
                        console.log(event.data);
                    };
                    //错误信息事件
                    worker.onerror = function(e){
                        console.log('erro: ' + e.message);
                        //终止线程
                        worker.terminate();
                    };
                };
            </script>
        </head>
        <body onload = "init()">
            
        </body>
    </html>

    worker.js

    //window对象的alert方法
    alert(1);
    onmessage = function(event){
        //向主线程发送event.data.name信息
        postMessage(event.data.name);
    };

    执行上面代码结果:

    为什么会这样呢?原因是alert为window对象的方法,所以会报错undefined。

    worker.js执行的上下文,与主页面html执行时的上下文并不相同,最顶层的对象并不是window,woker.js执行的全局上下文,是个叫做WorkerGlobalScope的东东,所以无法访问window、与window相关的DOM API,但是可以与setTimeout、setInterval等协作。
    WorkerGlobalScope作用域下的常用属性、方法如下:

    1、self

      我们可以使用 WorkerGlobalScope 的 self 属性来或者这个对象本身的引用

    2、location

      location 属性返回当线程被创建出来的时候与之关联的 WorkerLocation 对象,它表示用于初始化这个工作线程的脚步资源的绝对 URL,即使页面被多次重定向后,这个 URL 资源位置也不会改变。

    3、close

      关闭当前线程,与terminate作用类似

    4、importScripts

      我们可以通过importScripts()方法通过url在worker中加载库函数

    5、XMLHttpRequest

      有了它,才能发出Ajax请求

    6、setTimeout/setInterval以及addEventListener/postMessage

    四、关于worker

    我们可以做什么:

      1.可以加载一个JS进行大量的复杂计算而不挂起主进程,并通过postMessage,onmessage进行通信

      2.可以在worker中通过importScripts(url)加载另外的脚本文件

      3.可以使用 setTimeout(), clearTimeout(), setInterval(), and clearInterval()

      4.可以使用XMLHttpRequest来发送请求

      5.可以访问navigator的部分属性

    局限性:

      1.不能跨域加载JS

      2.worker内代码不能访问DOM

      3.各个浏览器对Worker的实现不大一致,例如FF里允许worker中创建新的worker,而Chrome中就不行

      4.IE这个新特性

  • 相关阅读:
    加密解密
    论事件驱动与异步IO
    linux 基础命令
    libgcc_s_dw2-1.dll 缺失问题解决
    TightVNC 远程桌面
    配置机器学习开发环境(eclipse + anaconda2)
    Caffe 执行python实例并可视化
    Caffe windows编译找不到python27_d.lib问题解决
    PHP 上传文件名中带中文的文件失败问题
    Windows 搭建PHP开发环境
  • 原文地址:https://www.cnblogs.com/giggle/p/5350288.html
Copyright © 2020-2023  润新知