JavaScript中的Web worker多线程API研究


Posted in Javascript onDecember 06, 2014

HTML5支持了Web Worker这样的API,允许网页在安全的情况下执行多线程代码。不过Web Worker实际上受到很多限制,因为它无法真正意义上共享内存数据,只能通过消息来做状态通知,所以甚至不能称之为真正意义上的“多线程”。

Web Worker的接口使用起来很不方便,它基本上自带一个sandbox,在沙箱中跑一个独立的js文件,通过 postMessage和 onMessge来和主线程通信:

var worker = new Worker("my.js");

var bundle = {message:'Hello world', id:1};

worker.postMessage(bundle); //postMessage可以传一个可序列化的对象过去

worker.onmessage = function(evt){

    console.log(evt.data);    //比较worker中传回来的对象和主线程中的对象

    console.log(bundle);  //{message:'Hello world', id:1}

}
//in my.js

onmessage = function(evt){

    var data = evt.data;

    data.id++;

    postMessage(data); //{message:'Hello world', id:2}

}

得到的结果可以发现,线程中得到的data的id增加了,但是传回来之后,并没有改变主线程的bundle中的id,因此,线程中传递的对象实际上copy了一份,这样的话,线程并没有共享数据,避免了读写冲突,所以是安全的。保证线程安全的代价就是限制了在线程中操作主线程对象的能力。

这样一个有限的多线程机制使用起来是很不方便的,我们当然希望Worker能够支持让代码看起来具有同时操作多线程的能力,例如,支持看起来像下面这个样子的代码:

var worker = new ThreadWorker(bundle /*shared obj*/);
worker.run(function(bundle){

    //do sth in worker thread...

    this.runOnUiThread(function(bundle /*shared obj*/){

        //do sth in main ui thread...

    });

    //...

});

这段代码里面,我们启动一个worker之后,能够让任意代码跑在worker中,并且当需要操作ui线程(比如读写dom)时,可以通过this.runOnUiThread回到主线程执行。

那么如何实现这个机制呢? 看下面的代码:

function WorkerThread(sharedObj){

    this._worker = new Worker("thread.js");

    this._completes = {};

    this._task_id = 0;

    this.sharedObj = sharedObj;
    var self = this;

    this._worker.onmessage = function(evt){

        var ret = evt.data;

        if(ret.__UI_TASK__){

            //run on ui task

            var fn = (new Function("return "+ret.__UI_TASK__))();

            fn(ret.sharedObj);

        }else{

            self.sharedObj = ret.sharedObj;

            self._completes[ret.taskId](ret);

        }

    }

}
WorkerThread.prototype.run = function(task, complete){

    var _task = {__THREAD_TASK__:task.toString(), sharedObj: this.sharedObj, taskId: this._task_id};

    this._completes[this._task_id++] = complete;

    this._worker.postMessage(_task);

}

上面这段代码定义了一个ThreadWorker对象,这个对象创建了一个运行thread.js的Web Worker,保存了共享对象SharedObj,并且对thread.js发回的消息进行处理。

如果thread.js中传回了一个UI_TASK消息,那么运行这个消息传过来的function,否则执行run的complete回调 我们看看thread.js是怎么写的:

onmessage = function(evt){

    var data = evt.data;
    if(data && data.__THREAD_TASK__){

        var task = data.__THREAD_TASK__;

        try{

            var fn = (new Function("return "+task))();
            var ctx = {

                threadSignal: true,

                sleep: function(interval){

                    ctx.threadSignal = false;

                    setTimeout(_run, interval);

                },

                runOnUiThread: function(task){

                    postMessage({__UI_TASK__:task.toString(), sharedObj:data.sharedObj});

                }

            }
            function _run(){

                ctx.threadSignal = true;

                var ret = fn.call(ctx, data.sharedObj);

                postMessage({error:null, returnValue:ret, __THREAD_TASK__:task, sharedObj:data.sharedObj, taskId: data.taskId});

            }
            _run(0);
        }catch(ex){

            postMessage({error:ex.toString() , returnValue:null, sharedObj: data.sharedObj});

        }

    }

}

可以看到,thread.js接收ui线程传过来的消息,其中最重要的是THREAD_TASK,这是ui线程传过来的需要worker线程执行的“任务”,由于function是不可序列化的,因此传递的是字符串,worker线程通过解析字符串成function来执行主线程提交的任务(注意在任务中将共享对象sharedObj传入),执行完成后将返回结果通过message传给ui线程。我们仔细看一下除了返回值returnValue以外,共享对象sharedObj也会被传回,传回时,由于worker线程和ui线程并不共享对象,因此我们人为通过赋值的方式同步两边的对象(这样是否线程安全?为什么?)

可以看到整个过程其实并不复杂,这么实现之后,这个ThreadWorker可以有以下两种用法:

var t1 = new WorkerThread({i: 100} /*shared obj*/);
        setInterval(function(){

            t1.run(function(sharedObj){

                    return sharedObj.i++;

                },

                function(r){

                    console.log("t1>" + r.returnValue + ":" + r.error);

                }

            );

        }, 500);

var t2 = new WorkerThread({i: 50});
        t2.run(function(sharedObj){   

            while(this.threadSignal){

                sharedObj.i++;
                this.runOnUiThread(function(sharedObj){

                    W("body ul").appendChild("<li>"+sharedObj.i+"</li>");

                });
                this.sleep(500);

            }

            return sharedObj.i;

        }, function(r){

            console.log("t2>" + r.returnValue + ":" + r.error);

        });

这样的用法从形式和语义上来说都让代码具有良好的结构,灵活性和可维护性。

好了,关于Web Worker的用法探讨就介绍到这里,有兴趣的同学可以去看一下这个项目:https://github.com/akira-cn/WorkerThread.js (由于Worker需要用服务器测试,我特意在项目中放了一个山寨的httpd.js,是个非常简陋的http服务的js,直接用node就可以跑起来)。

Javascript 相关文章推荐
js 关于=+与+=日期函数使用说明(赋值运算符)
Nov 15 Javascript
javascript学习笔记(七)Ajax和Http状态码
Oct 08 Javascript
js格式化时间的方法
Dec 18 Javascript
jQuery实现移动端手机商城购物车功能
Sep 24 Javascript
jQuery动态添加与删除tr行实例代码
Oct 18 Javascript
javascript事件的绑定基础实例讲解(34)
Feb 14 Javascript
Vue组件之全局组件与局部组件的使用详解
Oct 09 Javascript
微信小程序商品详情页规格属性选择示例代码
Oct 30 Javascript
利用vue + element实现表格分页和前端搜索的方法
Dec 25 Javascript
angularJS1 url中携带参数的获取方法
Oct 09 Javascript
从组件封装看Vue的作用域插槽的实现
Feb 12 Javascript
ES6 Object属性新的写法实例小结
Jun 25 Javascript
JavaScript实现的一个日期格式化函数分享
Dec 06 #Javascript
JavaScript实现twitter puddles算法实例
Dec 06 #Javascript
JavaScript实现的一个计算数字步数的算法分享
Dec 06 #Javascript
angularjs中的e2e测试实例
Dec 06 #Javascript
angularjs中的单元测试实例
Dec 06 #Javascript
angularjs指令中的compile与link函数详解
Dec 06 #Javascript
angularjs的一些优化小技巧
Dec 06 #Javascript
You might like
PHP5新特性: 更加面向对象化的PHP
2006/11/18 PHP
非集成环境的php运行环境(Apache配置、Mysql)搭建安装图文教程
2016/04/12 PHP
浅谈php处理后端&amp;接口访问超时的解决方法
2016/10/29 PHP
php 文件下载 出现下载文件内容乱码损坏的解决方法(推荐)
2016/11/16 PHP
php实现对短信验证码发送次数的限制实例讲解
2021/03/04 PHP
javascript 哈希表(hashtable)的简单实现
2010/01/20 Javascript
JavaScript 参数中的数组展开 [译]
2012/09/21 Javascript
javascript基础之查找元素的详细介绍(访问节点)
2013/07/05 Javascript
js禁止页面刷新禁止用F5键刷新禁止右键的示例代码
2013/09/23 Javascript
JS控制图片翻转示例代码(兼容firefox,ie,chrome)
2013/12/19 Javascript
js正则表达式验证邮件地址
2015/11/12 Javascript
js实现的星星评分功能函数
2015/12/09 Javascript
基于jQuery仿淘宝产品图片放大镜特效
2020/10/19 Javascript
JS控制弹出悬浮窗口(一览画面)的实例代码
2016/05/30 Javascript
老生常谈onBlur事件与onfocus事件(js)
2016/07/09 Javascript
微信小程序通过api接口将json数据展现到小程序示例
2017/01/20 Javascript
jQuery 添加样式属性的优先级别方法(推荐)
2017/06/08 jQuery
微信小程序开发教程之增加mixin扩展
2017/08/09 Javascript
ElementUI radio组件选中小改造
2019/08/12 Javascript
浅谈JS中this在各个场景下的指向
2019/08/14 Javascript
浅谈VUE中演示v-for为什么要加key
2020/01/16 Javascript
[49:31]DOTA2-DPC中国联赛 正赛 Elephant vs LBZS BO3 第二场 1月29日
2021/03/11 DOTA
Python Django 前后端分离 API的方法
2019/08/28 Python
Python使用sys.exc_info()方法获取异常信息
2020/07/23 Python
用gpu训练好的神经网络,用tensorflow-cpu跑出错的原因及解决方案
2021/03/03 Python
巴西食品补充剂在线零售商:Músculos na Web
2017/08/07 全球购物
Allsole美国/加拿大:英国一家专门出售品牌鞋子的网站
2018/10/21 全球购物
澳洲CFL商城:CHEMIST FOR LESS(中文)
2021/02/28 全球购物
三八节主持词
2014/03/17 职场文书
捐款倡议书格式范文
2014/05/14 职场文书
教师年度个人总结
2015/02/11 职场文书
2015年大学生实习评语
2015/03/25 职场文书
2015年教师工作总结范文
2015/03/31 职场文书
音乐课外活动总结
2015/05/09 职场文书
小学数学新课改心得体会
2016/01/22 职场文书
Python基础之变量的相关知识总结
2021/06/23 Python