`
hoodng
  • 浏览: 19966 次
社区版块
存档分类
最新评论

关于Web Worker应用的一个想法和实现

阅读更多

这篇文章以前在CU上,那边人气不够,重新发到这里。嘿嘿

 

        公司的产品需要在后端维护着一次会话的状态,而当用户关闭浏览器的时候,需要及时释放资源,这以前是通过浏览器window的load和unload事件分别触发CancelCloseAction和StartCloseAction来实现的。后来发现在有些情况下,比如“杀浏览器进程”,“网络异常”等时候,后台就无法确定浏览器的状态了,以至于不能及时释放资源。所以在另一个产品里,我们就使用了“心跳”监测技术,也就是前端定时(比如15~30秒),向后端发一个心跳,如果后端在特定时间里收不到心跳,就认为浏览器关闭了,此时可以回收该会话的所有资源。一般情况下,这个机制是可以工作的,但某些case,前端Javascript的运算量可能会非常大,这时心跳信号往往被延后,以至于时有session过期的情形出现。每每此时,我们多希望Javascript是多线程的,那该多好啊,可以专门开一个线程来发心跳。


1、封装Web Worker的设想
       
    看了网络上无数的抄来转去,实则内容同样的关于Web Worker的介绍后,我们已经知道,Web Worker的确是真正意义上的多线程了。但看着如此简单的postMessage和onmessage的例子,我实在难以确定该如何使用它,又如何用它来实现想象中的“心跳”线程呢。
       
    另外浏览器兼容问题,IE,万恶的IE依旧特立独行地不支持Web Worker,怎么办? 好消息是从IE8开始,IE开始支持frame间通过postMessage和onmessage来通信了, 这至少可以为模拟一个Web Worker在API层面上提供了一个可能。
       
    Java的java.lang.Thread已经是一个非常好的线程应用模型了。如果把Web Worker包装成一个java.lang.Thread会怎么样呢?我想信,至少对于Java程序员来说估计是很好用的。比如:

// 1、定义计算任务,可以理解成java.lang.Runnable 
var task = {
    context: { x:1 }, // 需要计算的东西
      
    run: function(){ // 对context进行计算的方法
        this.context.x++;
        // 如有需要可以向外post消息
    } 
};
  
// 2、创建线程对象
var myThread = new js.lang.Thread(task);
 
// 3、为线程对象绑定onmessage事件
myThead.onmessage = function(e){ // 处理线程运行中发出来的消息
 
    // 如有必要终止线程this.stop(); 
};
  
// 4、启动线程
myThread.start();
  
// 5、如有需要可以向线程提交另一个计算任务
myThread.submitTask(task);
    从以上js.lang.Thread的设想来看,包装后的Web Worker应该是非常容易用来进行多线程计算的,Thread提供的是计算能力,而数据和计算方法由调用者来决定。

2、Web Worker(Iframe Worker)应该如何工作
       
    在第1步的设想下,Web Worker将被封装在js.lang.Thread里,那么task,即Runnable,需要发送给Worker,而Worker的代码看起来,应该会象这样:
  // onmessage的实际句柄
  var _onmessage = function(e){
      
      /*
       * 我们期望e.data拿到的是如下一样的从var thi$ ...到 }的一个string
       *
       * var thi$ = {
       *
       *     context: {}, // 计算对象
       *
       *     run : function(){
       *         // 计算方法
       *     }
       * } 
       */ 
       
      eval(e.data); // 非常关键
  
      // 此时,我们有了一个thi$对象,执行计算
      thi$.run(); //或者thi$.run.call(thi$);
    
  };

3、一个实验性的js.lang.Thread实现

    Java程序员或着看过我以前文章的人,基本上都可以看懂下面Thread的实现。当然有一些如J$VM,js.lang.Class之类的和我的一个开源项目有关,有兴趣的可以到Google code看一下。
/**
 * The <code>Thread</code> for easily using Web Worker, and for the
 * IE8/9 use a iframe simulate Web Worker
 * 
 * Runnable :{
 *     context: xxx,
 *     run : function
 * }
 * 
 */
js.lang.Thread = function(Runnable){
    
    var worker, runnable;

    var _onmessage = function(e){
        var evt = e.getData(), data;
        if(evt.source == self) return;
                
        try{
            data = JSON.parse(evt.data);
        } catch (x) {
            data = evt.data;
        }
        
        if(js.lang.Class.typeOf(data) == "array"){
            // [msgId, msgData, [recvs], null, pri]
            switch(data[0]){
            case "console_inf":
                J$VM.System.out.println(data[1]);
                break;
            case "console_err":
                J$VM.System.err.println(data[1]);
                break;
            case "console_log":
                J$VM.System.log.println(data[1]);
                break;
            default:
                var fun = "on"+data[0];
                if(typeof this[fun] == "function"){
                    this[fun](data[1]);
                }
            }
        }else{
            if(typeof this.onmessage == "function"){
                this.onmessage(data);
            }
        }
    };

    var _onerror = function(e){
        var evt = e.getData(), data;
        if(evt.source == self) return;
        J$VM.System.err.println(evt.data);
    };
    
    /**
     * Submit new task to the thread
     * 
     * @param task It should be a <code>Runnable</code> or a 
     * <code>context</code> in <code>Runnable</code>
     * @param isRunnable indicates whether the first parameter "task"
     * is a <code>Runnable</code>
     */
    this.submitTask = function(task, isRunnable){
        if(task == undefined || task == null) return;
        isRunnable = isRunnable || false;
        
        var context, run;
        if(isRunnable){
            context = task.context;
            run = task.run;
        }else{
            context = task;
            run = runnable.run;
        }

        var buf = new js.lang.StringBuffer();
        buf.append("var thi$ = {");
        buf.append("context:").append(JSON.stringify(context));
        buf.append(",run:").append(run);
        buf.append("}");

        var msg = buf.toString();
        //J$VM.System.err.println("Thread post msg: "+msg);
        if(self.Worker){
            worker.postMessage(msg);
        }else{
			// IE must
            worker.postMessage(msg, "*");
        }

    };
    
    /**
     * Start the thread
     */
    this.start = function(){
        this.submitTask(runnable, true);
    };
    
    /**
     * Stop the thread
     */
    this.stop = function(){
        if(worker.terminate){
            worker.terminate();
        }else{
            // For the iframe worker, what should be we do ?
        }
    };

    var _init = function(Runnable){
        runnable = Runnable || {
            context:{}, 
            
            run:function(){
                J$VM.System.out.println("Web Worker is running");
            }};
        
        var E = js.util.Event;
        var path = J$VM.env["j$vm_home"]+"/classes/js/util/";

        if(self.Worker){
            worker = new Worker(path+"Worker.js");
        }else{
            // iframe ?
            var iframe = document.createElement("iframe");
            iframe.style.cssText = "visibility:hidden;border:0;width:0;height:0;";
            document.body.appendChild(iframe);
            var text = "<html><head>" +
                "<meta http-equiv='X-UA-Compatible' content='IE=edge'>" +
                "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>"+
                "</head></html>";
            var doc = iframe.contentDocument, head, script;
            doc.open();
            doc.write(text);
            doc.close();

            head = doc.getElementsByTagName("head")[0];
            text = js.lang.Class.getResource(J$VM.env["j$vm_home"]+"/jsre.js");
            script = doc.createElement("script");
            script.type = "text/javascript";
            script.id = "j$vm";
			// Becase we don't use src to load the iframe, but the J$VM will use
			// "src" attribute to indicates j$vm home. So we use a special attribute
            // name "crs" at here. @see also js.lang.System#_buildEnv 
            script.setAttribute("crs",J$VM.env["j$vm_home"]+"/jsre.js");
            script.setAttribute("classpath","");
            script.text = text;
            text = js.lang.Class.getResource(path + "Worker.js");
            script.text += text;
            head.appendChild(script);
            head.removeChild(script);

            worker = iframe.contentWindow;
        }

        E.attachEvent(worker, "message", 0, this, _onmessage);
        E.attachEvent(worker, "error", 0, this, _onerror);

    };

    _init.$bind(this)(Runnable);
    
}.$extend(js.lang.Object);
 

4、Worker.js的实现
      
     Worker.js是Web Worker或IFrame Worker需要加载来处理onmessage的,在我的实现中,它并不是象js.lang.Thread一样,是一个通常意义的的类,而是简单的一个javascript文件,只是被管理在js.util的这个目录下:
  1. var isWebWorker = function(){
        try{return (window) ? false : true;} catch (x) {return true;}
    }();
    
    var _onmessage = function(e){
        if(isWebWorker){
            eval(e.data);
        }else{
            var _e = e.getData();
            if(_e.source == self) return;
    
            eval(_e.data);
        }
    
        thi$.run.call(thi$);
    };
    
    if(isWebWorker){
        importScripts("../../../jsre.js");
        onmessage = _onmessage;
    }else{
        js.util.Event.attachEvent(window, "message", 0, this, _onmessage);
    }
    
     
5、结论

    以上代码,已经发布到Google Code上,对IE8+, Firefox, Chrome浏览器进行过测试,工作正常。
      
     以后在Javascript里,终于可以象在Java里一样使用多线程计算技术了,当然Web Worker本身的限制是不可避免的,比如不能访问DOM啦。


1
1
分享到:
评论
1 楼 stamen 2012-11-29  
挺不错的,设计很精巧!

相关推荐

    开源社交平台:一个开源的去中心化微博社交平台,完美适配了PC与移动端界面 支持丰富且可定制化的WebUI、Emoji表情

    作为一款去中心化的应用,Misskey不依赖于单一的服务器,而是由多个节点组成,用户可以在这些节点之间自由切换,实现数据的分布式存储和交换。这种模式避免了传统社交媒体的数据集中问题,提高了用户对个人信息的...

    PWA-Asp.NetCore::beer_mug:ASP.NET CORE 2.0 MVC渐进式Web应用程序

    PWA Asp.NetCore 一个演示,以演示带有ASP.Net Core 2.0的渐进式Web应用程序的强大功能。 本演示使用RSS Reader作为整合PWA的想法。什么是渐进式Web应用程序渐进式Web应用程序是结合了最好的Web应用程序和最好的应用...

    mart-441:2021年Web技术类的回购

    5. **Web API和浏览器兼容性**:随着新的API如Fetch API、Web Storage、Web Workers和Service Worker的推广,开发者可以实现更强大的功能,如离线应用、后台处理和数据缓存。 6. **性能优化**:包括代码分割、懒...

    PWA创意组织者

    3. **Web App Manifest**:这是一个JSON文件,包含了关于应用的信息,如应用名称、图标、启动屏幕设置等,允许PWA在用户的主屏幕上以类似原生应用的形式显示。 4. **HTTPS**:PWA需要通过安全的HTTPS协议提供服务,...

    手机博客_博客_greaty3t_手机博客源码_手机博客_手机端博客_

    3. **离线支持**:利用Service Worker和IndexedDB等Web存储技术,手机博客可以实现离线阅读和写作,即使在网络不稳定的情况下也能正常工作。 4. **社交网络集成**:集成社交媒体分享按钮,方便用户将博客内容分享到...

    sandbox:简单的沙箱,用于证明概念的一些想法

    在IT行业中,沙箱(Sandbox)是一种非常重要的技术,特别是在软件开发、安全测试...通过理解沙箱的工作原理和实现方式,我们可以更好地保护应用程序免受潜在的恶意代码威胁,同时在开发过程中探索和验证新的编程概念。

    mercury:无服务器、干净且持久的笔记转储,可完全脱机工作

    总的来说,Mercury是一款基于JavaScript开发的、利用服务工作者技术实现离线功能的笔记应用,其设计目标是提供一个简单、持久且无需服务器的笔记解决方案,让用户能够在任何环境下自由记录和访问他们的想法和信息。...

    ClusterWS-Examples:借助示例应用程序,可以更轻松地通过ClusterWS进行陈述

    ClusterWS 是一个JavaScript库,专为构建分布式系统和实时应用而设计,特别是在Web服务环境中,它提供了WebSocket集群功能。WebSocket是一种在客户端和服务器之间建立持久连接的协议,允许双方进行双向通信,使得...

    HTML5特酷图片展示

    1. **Canvas元素**:HTML5引入了`&lt;canvas&gt;`元素,这是一个可编程的画布,允许开发者通过JavaScript绘制图形,包括2D和3D图像。在“3D图片旋转”和“3D骨牌图片特效”中,`&lt;canvas&gt;`元素可能是实现这些效果的基础,...

    nihon:Nihon 尝试制作基于网络的图片、声音和视频演示器

    7. **API和扩展**:作为一个基于JavaScript的应用,Nihon 可能开放了API,允许开发者自定义功能或与其他Web服务集成,比如将演示文稿导出为其他格式,或者集成社交媒体分享按钮。 在"nihon-master"这个压缩包文件中...

    addon-playground:玩具和概念验证 Firefox 附加组件的集合

    通过研究这个项目,开发者不仅可以学习到如何使用JavaScript编写Firefox附加组件,还可以了解到如何设计和实现各种Web功能,如数据存储、定时任务、跨域请求等。此外,这个项目也可能涉及到了一些最新的Web标准和...

    scraps:实验什么的

    在“scraps”这个项目中,我们可以看到一个关于实验性质的工作集合,主要涉及的是Chrome浏览器相关的探索和测试。由于项目标题是"scraps:实验什么的",这暗示了里面可能包含了一些开发者进行的尝试性工作,比如新的...

    vue-image-editor:Vue.js的图像编辑器

    仍然可以通过实现Web Worker或其他多线程方法在性能方面进行持续改进。 图像数据作为Blob对象保存在内存中。 与服务器处理的图像相比的优势: 隐私-&gt;图片数据在编辑过程中始终不会离开用户的计算机 速度-&gt;初始...

    artscience2015.github.io

    【描述】由于描述只给出了网站的名称,我们可以推断这可能是2015年一个关于艺术与科学结合的在线平台,可能是为了分享创新的想法、项目成果或者技术讨论。不过,没有具体信息,我们只能基于通用的GitHub项目网站特点...

    Euphoria-Post:这是一个发布网站(“您可以随意发布任何内容的地方”)网站

    总而言之,Euphoria-Post是一个基于JavaScript构建的跨浏览器发布平台,它通过灵活的前端技术实现了自由发布和良好的用户体验。开发者在设计时考虑到了不同浏览器的兼容性,确保用户无论使用何种设备或浏览器都能...

    我的博客

    【标题】: "我的博客" 是一个以个人视角分享日常思考和专业知识的平台。这个博客主要专注于编程领域的技术分享,尤其是 JavaScript 这一强大的前端开发语言。通过此平台,作者可以展示自己的学习成果,与其他开发者...

    lahacks2021

    【lahacks2021】是一个可能代表编程竞赛或黑客马拉松活动的名称,这通常涉及到一系列的编程挑战和项目,旨在提升参赛者的技术能力。在这个特定的案例中,标签为"JavaScript",意味着活动可能围绕JavaScript语言进行...

    Smart-Text-Editor:只需浏览器和键盘的文本编辑器!

    我已经开始实现类似于渐进式Web应用程序的功能,因此可以像大多数网站一样通过浏览器打开/使用它,或者尽可能接近普通的台式机/智能手机/平板电脑应用程序! 我个人认为这个想法非常有趣,因此我将继续对其进行更多...

    高级前端工程师简历模板

    9. **持续学习和创新**: 高级工程师应关注前端领域的最新动态,如WebAssembly、Service Worker、Web Components等新兴技术,并能在合适的时候引入到项目中。 10. **团队协作与沟通**: 作为高级工程师,你需要具备...

Global site tag (gtag.js) - Google Analytics