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 事件
å¨å®¢æ·ç«¯ç 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 æ¹æ³åéæ°æ®
å¨åçªå£ä¸çå¬ 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 ä¸éè¿æµè¯ã