这篇文章以前在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的这个目录下:
-
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啦。
分享到:
相关推荐
作为一款去中心化的应用,Misskey不依赖于单一的服务器,而是由多个节点组成,用户可以在这些节点之间自由切换,实现数据的分布式存储和交换。这种模式避免了传统社交媒体的数据集中问题,提高了用户对个人信息的...
PWA Asp.NetCore 一个演示,以演示带有ASP.Net Core 2.0的渐进式Web应用程序的强大功能。 本演示使用RSS Reader作为整合PWA的想法。什么是渐进式Web应用程序渐进式Web应用程序是结合了最好的Web应用程序和最好的应用...
5. **Web API和浏览器兼容性**:随着新的API如Fetch API、Web Storage、Web Workers和Service Worker的推广,开发者可以实现更强大的功能,如离线应用、后台处理和数据缓存。 6. **性能优化**:包括代码分割、懒...
3. **Web App Manifest**:这是一个JSON文件,包含了关于应用的信息,如应用名称、图标、启动屏幕设置等,允许PWA在用户的主屏幕上以类似原生应用的形式显示。 4. **HTTPS**:PWA需要通过安全的HTTPS协议提供服务,...
3. **离线支持**:利用Service Worker和IndexedDB等Web存储技术,手机博客可以实现离线阅读和写作,即使在网络不稳定的情况下也能正常工作。 4. **社交网络集成**:集成社交媒体分享按钮,方便用户将博客内容分享到...
在IT行业中,沙箱(Sandbox)是一种非常重要的技术,特别是在软件开发、安全测试...通过理解沙箱的工作原理和实现方式,我们可以更好地保护应用程序免受潜在的恶意代码威胁,同时在开发过程中探索和验证新的编程概念。
总的来说,Mercury是一款基于JavaScript开发的、利用服务工作者技术实现离线功能的笔记应用,其设计目标是提供一个简单、持久且无需服务器的笔记解决方案,让用户能够在任何环境下自由记录和访问他们的想法和信息。...
ClusterWS 是一个JavaScript库,专为构建分布式系统和实时应用而设计,特别是在Web服务环境中,它提供了WebSocket集群功能。WebSocket是一种在客户端和服务器之间建立持久连接的协议,允许双方进行双向通信,使得...
1. **Canvas元素**:HTML5引入了`<canvas>`元素,这是一个可编程的画布,允许开发者通过JavaScript绘制图形,包括2D和3D图像。在“3D图片旋转”和“3D骨牌图片特效”中,`<canvas>`元素可能是实现这些效果的基础,...
7. **API和扩展**:作为一个基于JavaScript的应用,Nihon 可能开放了API,允许开发者自定义功能或与其他Web服务集成,比如将演示文稿导出为其他格式,或者集成社交媒体分享按钮。 在"nihon-master"这个压缩包文件中...
通过研究这个项目,开发者不仅可以学习到如何使用JavaScript编写Firefox附加组件,还可以了解到如何设计和实现各种Web功能,如数据存储、定时任务、跨域请求等。此外,这个项目也可能涉及到了一些最新的Web标准和...
在“scraps”这个项目中,我们可以看到一个关于实验性质的工作集合,主要涉及的是Chrome浏览器相关的探索和测试。由于项目标题是"scraps:实验什么的",这暗示了里面可能包含了一些开发者进行的尝试性工作,比如新的...
仍然可以通过实现Web Worker或其他多线程方法在性能方面进行持续改进。 图像数据作为Blob对象保存在内存中。 与服务器处理的图像相比的优势: 隐私->图片数据在编辑过程中始终不会离开用户的计算机 速度->初始...
【描述】由于描述只给出了网站的名称,我们可以推断这可能是2015年一个关于艺术与科学结合的在线平台,可能是为了分享创新的想法、项目成果或者技术讨论。不过,没有具体信息,我们只能基于通用的GitHub项目网站特点...
总而言之,Euphoria-Post是一个基于JavaScript构建的跨浏览器发布平台,它通过灵活的前端技术实现了自由发布和良好的用户体验。开发者在设计时考虑到了不同浏览器的兼容性,确保用户无论使用何种设备或浏览器都能...
【标题】: "我的博客" 是一个以个人视角分享日常思考和专业知识的平台。这个博客主要专注于编程领域的技术分享,尤其是 JavaScript 这一强大的前端开发语言。通过此平台,作者可以展示自己的学习成果,与其他开发者...
【lahacks2021】是一个可能代表编程竞赛或黑客马拉松活动的名称,这通常涉及到一系列的编程挑战和项目,旨在提升参赛者的技术能力。在这个特定的案例中,标签为"JavaScript",意味着活动可能围绕JavaScript语言进行...
我已经开始实现类似于渐进式Web应用程序的功能,因此可以像大多数网站一样通过浏览器打开/使用它,或者尽可能接近普通的台式机/智能手机/平板电脑应用程序! 我个人认为这个想法非常有趣,因此我将继续对其进行更多...
9. **持续学习和创新**: 高级工程师应关注前端领域的最新动态,如WebAssembly、Service Worker、Web Components等新兴技术,并能在合适的时候引入到项目中。 10. **团队协作与沟通**: 作为高级工程师,你需要具备...