web workers
web workers 简介
至 2008 年 w3c 制定出第一个 html5 草案开始,html5 承载了越来越多崭新的特性和功能。它不但强化了 web 系统或网页的表现性能,而且还增加了对本地数据库等 web 应用功能的支持。其中,最重要的一个便是对多线程的支持。在 html5 中提出了工作线程(web workers)的概念,并且规范出 web workers 的三大主要特征:能够长时间运行(响应),理想的启动性能以及理想的内存消耗。web workers 允许开发人员编写能够长时间运行而不被用户所中断的后台程序,去执行事务或者逻辑,并同时保证页面对用户的及时响应。
web workers 为 web 前端网页上的脚本提供了一种能在后台进程中运行的方法。一旦它被创建,web workers 就可以通过 postmessage 向任务池发送任务请求,执行完之后再通过 postmessage 返回消息给创建者指定的事件处理程序 ( 通过 onmessage 进行捕获 )。web workers 进程能够在不影响用户界面的情况下处理任务,并且,它还可以使用 xmlhttprequest 来处理 i/o,但通常,后台进程(包括 web workers 进程)不能对 dom 进行操作。如果希望后台程序处理的结果能够改变 dom,只能通过返回消息给创建者的回调函数进行处理。
浏览器对 html5 支持情况可以参考网站 when can i use...
在 web workers 中使用 postmessage 和 onmessage
首先,需要在客户端页面的 javascript 代码中 new 一个 worker 实例出来,参数是需要在另一个线程中运行的 javascript 文件名称。然后在这个实例上监听 onmessage 事件。最后另一个线程中的 javascript 就可以通过调用 postmessage 方法在这两个线程间传递数据了。
清单 1. 主线程中创建 worker 实例,并监听 onmessage 事件
test web worker
å¨å®¢æ·ç«¯ç compute.js ä¸ï¼åªæ¯ç®åçéå¤å¤æ¬¡å åæä½ï¼æåéè¿ postmessage æ¹æ³æç»æè¿åç»ä¸»çº¿ç¨ï¼ç®çå°±æ¯çå¾
䏿®µæ¶é´ãèå¨è¿æ®µæ¶é´å
ï¼ä¸»çº¿ç¨ä¸åºè¯¥è¢«é»å¡ï¼ç¨æ·å¯ä»¥éè¿ææ½æµè§å¨ï¼åå¤§ç¼©å°æµè§å¨çªå£çæä½æµè¯è¿ä¸ç°è±¡ãè¿ä¸ªéé»å¡ä¸»çº¿ç¨çç»æå°±æ¯ web workers æ³è¾¾å°çç®çã
æ¸
å 2. compute.js ä¸è°ç¨ postmessage æ¹æ³è¿å计ç®ç»æ
var i=0;
function timedcount(){
for(var j=0,sum=0;j for(var i=0;i sum+=i;
}
}
// è°ç¨ postmessage å主线ç¨åéæ¶æ¯
postmessage(sum);
}
postmessage(before computing,+new date());
timedcount();
postmessage(after computing,+new date());
å¾ 1. æµè§å¨ä¸è¿è¡ç»æ
cross-document messaging
cross-document messaging ç®ä»
ç±äºåæºçç¥çéå¶ï¼javascript è·¨åçé®é¢ï¼ä¸ç´æ¯ä¸ä¸ªé¢ä¸ºæ£æçé®é¢ãhtml5 æä¾äºå¨ç½é¡µææ¡£ä¹é´äºç¸æ¥æ¶ä¸åéä¿¡æ¯çåè½ã使ç¨è¿ä¸ªåè½ï¼åªè¦è·åå°ç½é¡µæå¨çªå£å¯¹è±¡çå®ä¾ï¼ä¸ä»
åæºï¼å + 端å£å·ï¼ç web ç½é¡µä¹é´å¯ä»¥äºç¸éä¿¡ï¼çè³å¯ä»¥å®ç°è·¨åéä¿¡ã è¦æ³æ¥æ¶ä»å
¶ä»çªå£å鿥çä¿¡æ¯ï¼å¿
须对çªå£å¯¹è±¡ç onmessage äºä»¶è¿è¡çå¬ï¼å
¶å®çªå£å¯ä»¥éè¿ postmessage æ¹æ³æ¥ä¼ éæ°æ®ãè¯¥æ¹æ³ä½¿ç¨ä¸¤ä¸ªåæ°ï¼ç¬¬ä¸ä¸ªåæ°ä¸ºæåéçæ¶æ¯ææ¬ï¼ä½ä¹å¯ä»¥æ¯ä»»ä½ javascript 对象ï¼éè¿ json 转æ¢å¯¹è±¡ä¸ºææ¬ï¼ï¼ç¬¬äºä¸ªåæ°ä¸ºæ¥æ¶æ¶æ¯ç对象çªå£ç url å°åï¼å¯ä»¥å¨ url å°åå符串ä¸ä½¿ç¨éé
符'*'æå®å
¨é¨å°ã
å¨ cross-document messaging ä¸ä½¿ç¨ postmessage å onmessage
为äºå®ç°ä¸ååä¹é´çéä¿¡ï¼éè¦å¨æä½ç³»ç»ç hosts æä»¶æ·»å 两个ååï¼è¿è¡æ¨¡æã
æ¸
å 3. hosts æä»¶ä¸æ·»å 两个ä¸åçåå
127.0.0.1 parent.com
127.0.0.1 child.com
å¨ç¶ç½é¡µä¸éè¿ iframe åµå
¥å页é¢ï¼å¹¶å¨ javascript 代ç ä¸è°ç¨ postmessage æ¹æ³åéæ°æ®å°åçªå£ã
æ¸
å 4. ç¶é¡µé¢ä¸åµå
¥å页é¢ï¼è°ç¨ postmessage æ¹æ³åéæ°æ®
test cross-domain communication using html5
id=otherpage>
value=send to child.com onclick=sendit() />
å¨åçªå£ä¸çå¬ onmessage äºä»¶ï¼å¹¶ç¨ javascript å®ç°æ¾ç¤ºç¶çªå£åéè¿æ¥çæ°æ®ã
æ¸
å 5. åçªå£ä¸çå¬ onmessage äºä»¶ï¼æ¾ç¤ºç¶çªå£åéæ¥çæ°æ®
web page from child.com
web page from http://child.com:8080
å¾ 2. ç¶çªå£åµå
¥åçªå£
å¾ 3. ç¶çªå£åéæ°æ®å°åçªå£
websockets
websockets ç®ä»
å¨ web åºç¨ä¸ï¼http åè®®å³å®äºå®¢æ·ç«¯åæå¡ç«¯è¿æ¥æ¯çè¿æ¥ï¼å³å®¢æ·ç«¯ requestï¼æå¡ç«¯ responseï¼è¿æ¥æå¼ãè¦æ³å®ç°å®¢æ·ç«¯åæå¡ç«¯å®æ¶éä¿¡ï¼åªè½éè¿å®¢æ·ç«¯è½®è¯¢æ¥å®ç°ãæå¡ç«¯æ¨éæ°æ®ä¹å¹¶ä¸æ¯åé¢ä¸ææä¸çç´æ¥æ¨ï¼å
¶å®è¿æ¯å®¢æ·ç«¯èªå·±åãwebsockets æ¯ html5 è§èæ°å¼å
¥çåè½ï¼ç¨äºè§£å³æµè§å¨ä¸åå°æå¡å¨ååé讯çé®é¢ï¼ä½¿ç¨ websockets ææ¯ï¼åå°å¯ä»¥éæ¶åå端æ¨éæ¶æ¯ï¼ä»¥ä¿è¯ååå°ç¶æç»ä¸ã
å¨ websockets ä¸ä½¿ç¨ send å onmessage
ç±äºææ¬ä¸»è¦ä»ç» postmessage(send) å onmessage 客æ·ç«¯ api çåºç¨ï¼è websockets æ¶åå°æå¡å¨ç«¯ä»£ç çå®ç°ï¼æä»¥æ¬æå°éåæç®åçæå¡å¨ç«¯æ¡æ¶æ¥ç¼åæå¡å¨ä»£ç ãwebsockets æå¡å¨ç«¯æ jetty æä¾çåºäº java çå®ç°ï¼æ websocket-node åºäº node.js çå®ç°ï¼å¨ .net 4.5 ä¸ä¹ç´æ¥æä¾äº websockets çæ¯æãæ¬æå°ä½¿ç¨ websocket-node æä¾ç示ä¾ä»£ç ï¼ç¨ä½ä¿®æ¹ä½ä¸º websockets çæå¡å¨ç«¯ãå
³äº node.js çä»ç»ä»¥å使ç¨è¯·åè node.js 宿¹ç½ç« node.jsï¼å
³äº websocket-node ç使ç¨è¯·åè websocket-nodeã
é¦å
ï¼éè¦å¨å®¢æ·ç«¯éè¿ javascript 代ç new ä¸ä¸ª websocket å®ä¾åºæ¥ï¼åæ°æ¯å®ç° websocket æå¡å¨ç«¯ url å°åãç¶åå¨è¿ä¸ªå®ä¾ä¸çå¬ onmessage äºä»¶æ¥æ¶æå¡å¨ç«¯åéè¿æ¥çæ°æ®ãå½ç¶ï¼å®¢æ·ç«¯ä¹å¯ä»¥è°ç¨ send æ¹æ³ï¼åéæ°æ®å°æå¡å¨ç«¯ã
æ¸
å 6. å建 websocket 对象ï¼å¹¶çå¬ onmessage äºä»¶
connect : function() {
var location =ws://localhost:8000/;
// å建 websockets å¹¶ä¼ å
¥ websockets server å°å
this._ws =new websocket(location);
this._ws.onmessage=this._onmessage;
//websockets è¿æä¾äº onopen 以å onclose äºä»¶
this._ws.onopen =this._onopen;
this._ws.onclose =this._onclose;
}
å¨ _onmessage æ¹æ³ä¸ï¼æ¥æ¶æ°æ®ï¼å¹¶æ¾ç¤ºå¨é¡µé¢ä¸
æ¸
å 7. _onmessage æ¹æ³
_onmessage : function(event) {
//event 忰䏿 data 屿§ï¼å°±æ¯æå¡å¨åéè¿æ¥çæ°æ®
if (event.data) {
var messagebox = document.getelementbyid('messagebox');
var spantext = document.createelement('span');
spantext.classname ='text';
// ææå¡å¨åéè¿æ¥çæ°æ®æ¾ç¤ºå¨çªå£ä¸
spantext.innerhtml = event.data;
var linebreak = document.createelement('br');
messagebox.appendchild(spantext);
messagebox.appendchild(linebreak);
messagebox.scrolltop = messagebox.scrollheight
- messagebox.clientheight;
}
},
å¨ _onopen æ¹æ³ä¸ï¼è°ç¨ _send æ¹æ³åé䏿¡æ¶æ¯å°æå¡å¨ç«¯ï¼åä¹è¿æ¥å·²ç»å»ºç«ãå¨ _onclose æ¹æ³ä¸ï¼æ websocket çå®ä¾è®¾ç½®æ nullï¼éæ¾èµæºã
æ¸
å 8. _onopenï¼_onclose 以å send æ¹æ³
_onopen : function() {
server._send(client:open websockets,+new date());
},
//message åæ°å°±æ¯å®¢æ·ç«¯åæå¡å¨ç«¯åéçæ°æ®
_send : function(message) {
if (this._ws)
this._ws.send(message);
},
// æ¤æ¹æ³æä¾å¤é¨ä»£ç è°ç¨
send : function(text) {
if (text !=null&& text.length >0)
server._send(text);
},
_onclose : function(m) {
this._ws =null;
}
æè¿äºæ¹æ³å°è£
å¨ä¸ä¸ª server 对象ä¸ï¼æ¹ä¾¿æä¾å¤é¨è°ç¨ãç¨æ·åªéè¦å
è°ç¨ server ç connect æ¹æ³å»ºç«è¿æ¥ï¼ç¶åè°ç¨ send æ¹æ³åéæ°æ®ã
æ¸
å 9. å°è£
客æ·ç«¯å®ç°
var server = {
// 对å¤ä¸»è¦æä¾ connect å send æ¹æ³
connect : function() {...},
_onopen : function() {...},
_send : function(message) {...},
send : function(text) {...},
_onmessage : function(event) {...},
_onclose : function(m) {...}
};
卿å¡å¨ç«¯ï¼éè¿ javascript è¯è¨ç®åä¿®æ¹ websocket-node 䏿ä¾ç echo-server.js 示ä¾å³å¯ãè¿éåªå±ç¤ºå
³é®ä»£ç é¨åï¼å
¶å®ä»£ç 请åè§ websocket-node 示ä¾ã
æ¸
å 10. websockets æå¡å¨ç«¯ç®åå®ç°
// çå¬å®¢æ·ç«¯çè¿æ¥è¯·æ±
wsserver.on('connect', function(connection) {
function sendcallback(err) {
if (err) console.error(send() error: + err);
}
// çå¬å®¢æ·ç«¯åéæ°æ®ç请æ±
connection.on('message', function(message) {
if (message.type === 'utf8') {// åºå«å®¢æ·ç«¯åè¿æ¥çæ°æ®æ¯ææ¬è¿æ¯äºè¿å¶ç±»å
connection.sendutf(
server:get message:
+message.utf8data, sendcallback
);
}
else if (message.type === 'binary') {
connection.sendbytes(message.binarydata, sendcallback);
}
});
connection.on('close', function(reasoncode, description) {
});
});
å¾ 4. ç¹å» connect æé®
å¾ 5. è¾å
¥å
容ï¼åå» send message æé®
server-sent events
server-sent events ç®ä»
html5 server-sent äºä»¶æ¨¡åå
许æ¨ä»æå¡å¨ push 宿¶æ°æ®å°æµè§å¨ãæ¬ææä»¬å°ä»ç»å©ç¨ eventsource 对象å¤çä¸é¡µé¢é´çæ¥æ¶ååéæ°æ®ãå¨å®¢æ·ç«¯ï¼æä»¬ä½¿ç¨ html5+javascriptï¼æå¡ç«¯ä½¿ç¨ javaãå¨ç°åç ajax 模å¼ä¸ï¼web 页é¢ä¼æç»ä¸æå°è¯·æ±æå¡å¨ä¼ è¾æ°æ°æ®ï¼ç±å®¢æ·ç«¯è´è´£è¯·æ±æ°æ®ãè卿å¡ç«¯å鿍¡å¼ä¸ï¼æ éå¨å®¢æ·ç«¯ä»£ç 䏿§è¡è¿ç»çæ°æ®è¯·æ±ï¼èæ¯ç±æå¡ç«¯ push æ¨éæ´æ°ã䏿¦æ¨å¨é¡µé¢ä¸åå§åäº server-sent äºä»¶ï¼æå¡ç«¯èæ¬å°æç»å°åéæ´æ°ã客æ·ç«¯ javascript 代ç 䏿¦æ¥æ¶å°æ´æ°å°±å°æ°çæ°æ®åå
¥é¡µé¢ä¸å±ç¤ºåºæ¥ã
å¨ server-sent events ä¸ä½¿ç¨ onmessage
server-sent events å websockets æç¸åä¹å¤ï¼websockets å®ç°äºæå¡å¨ç«¯ä»¥å客æ·ç«¯çååéä¿¡åè½ï¼è server-sent events åä»
æ¯ææå¡å¨ç«¯å°å®¢æ·ç«¯çååéä¿¡ï¼èä¸ server-sent events åæ ·éè¦æå¡å¨ç«¯çå®ç°ï¼æ¬æå°ä½¿ç¨åºäº java ç servlet ææ¯å®ç°æå¡å¨ç«¯ãå
³äºæå¡å¨ç«¯å客æ·ç«¯åæ°æ®çæ ¼å¼ï¼å¯ä»¥åè w3c å
³äº server-sent events çè§èææ¡£ server-sent eventsãç±äºæ¯æå¡å¨ç«¯å°å®¢æ·ç«¯çååéä¿¡ï¼æä»¥å¨ server-sent events 䏿²¡æ postmessage æ¹æ³ã
é¦å
ï¼å¨å®¢æ·ç«¯éè¿ javascript 代ç new ä¸ä¸ª eventsource å®ä¾åºæ¥ï¼åæ°æ¯å®ç° eventsource æå¡å¨ç«¯ url å°åãç¶åå¨è¿ä¸ªå®ä¾ä¸çå¬ onmessage äºä»¶æ¥æ¶æå¡å¨ç«¯åéè¿æ¥çæ°æ®ã
æ¸
å 11. å建 eventsource 对象ï¼å¹¶çå¬ onmessage äºä»¶
if (!!window.eventsource) {
// å建 eventsource å®ä¾ï¼ä¼ å
¥ server å°å
var source = new eventsource('/testhtml5/serversentevent');
} else {
console.log(your browser doesn't support server-sent event);
}
// çå¬ message äºä»¶ï¼çå¾
æ¥æ¶æå¡å¨ç«¯åéè¿æ¥çæ°æ®
source.addeventlistener('message', function(event) {
//event 忰䏿 data 屿§ï¼å°±æ¯æå¡å¨åéè¿æ¥çæ°æ®
console.log(event.data);
}, false);
//eventsource è¿æä¾äº onopen 以å onerror äºä»¶
source.addeventlistener('open', function(event) {
}, false);
source.addeventlistener('error', function(event) {
if (event.readystate == eventsource.closed) {
}
}, false);
æå¡å¨ç«¯ï¼å¨ java è¯è¨å®ç°ç servlet doget æ¹æ³ä¸ä½¿ç¨ response 对象å客æ·ç«¯åæ°æ®
æ¸
å 12. æå¡å¨ç«¯ç®åå®ç°
// è¿éå¿
须设置 content-type 为 text/event-stream
response.setheader(content-type, text/event-stream);
response.setheader(cache-control, no-cache);
response.setcharacterencoding (utf-8);
string id = new date().tostring();
response.getwriter().println(id:+id);
// å客æ·ç«¯åä¸¤è¡æ°æ®
response.getwriter().println(data:server-sent event is working.);
response.getwriter().println(data:test server-sent event multi-line data);
response.getwriter().println();
response.getwriter().flush();
å¾ 6. server-sent events è¿è¡ç»æ
ç»æè¯
æ¬æè¯¦ç»ä»ç»äº postmessageï¼sendï¼å onmessage api å¨å®¢æ·ç«¯çåºç¨æ
åµï¼å¯ä»¥çå°å¨ä¸åçåºæ¯ä¸è¿ä¸¤ä¸ªæ¹æ³çåºç¨æ¨¡å¼é½æ¯ç±»ä¼¼çãpostmessage çä½ç¨å°±æ¯ä¼ éæ°æ®ï¼è onmessage çä½ç¨å°±æ¯æ¥æ¶æ°æ®ãææ¡æ¤ç» api 对以åå¼å html 5 åºç¨ç¨åºå°ä¼ææå¸®å©ãæ¬æ web workersï¼cross-document messagingï¼websockets ç代ç å¨ firefox 14 ä¸éè¿æµè¯ï¼server-sent events ç代ç å¨ chrome 16 ä¸éè¿æµè¯ã
