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 事件
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> <title>Test Web worker</title> <script type="text/JavaScript"> function init(){ var worker = new Worker('compute.js'); //event 参数中有 data 属性,就是子线程中返回的结果数据 worker.onmessage= function (event) { // 把子线程返回的结果添加到 div 上 document.getElementById("result").innerHTML += event.data+"<br/>"; }; } </script> </head> <body onload="init()"> <div id="result"></div> </body> </html>
在客户端的 compute.js 中,只是简单的重复多次加和操作,最后通过 postMessage 方法把结果返回给主线程,目的就是等待一段时间。而在这段时间内,主线程不应该被阻塞,用户可以通过拖拽浏览器,变大缩小浏览器窗口等操作测试这一现象。这个非阻塞主线程的结果就是 Web Workers 想达到的目的。
清单 2. compute.js 中调用 postMessage 方法返回计算结果
var i=0; function timedCount(){ for(var j=0,sum=0;j<100;j++){ for(var i=0;i<100000000;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 方法发送数据
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Test Cross-domain communication using HTML5</title> <script type="text/JavaScript"> function sendIt(){ // 通过 postMessage 向子窗口发送数据 document.getElementById("otherPage").contentWindow .postMessage( document.getElementById("message").value, "http://child.com:8080" ); } </script> </head> <body> <!-- 通过 iframe 嵌入子页面 --> <iframe src="http://child.com:8080/TestHTML5/other-domain.html" id="otherPage"></iframe> <br/><br/> <input type="text" id="message"><input type="button" value="Send to child.com" onclick="sendIt()" /> </body> </html>
在子窗口中监听 onmessage 事件,并用 JavaScript 实现显示父窗口发送过来的数据。
清单 5. 子窗口中监听 onmessage 事件,显示父窗口发送来的数据
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Web page from child.com</title> <script type="text/JavaScript"> //event 参数中有 data 属性,就是父窗口发送过来的数据 window.addEventListener("message", function( event ) { // 把父窗口发送过来的数据显示在子窗口中 document.getElementById("content").innerHTML+=event.data+"<br/>"; }, false ); </script> </head> <body> Web page from http://child.com:8080 <div id="content"></div> </body> </html>
图 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:<br/>"+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 对象向客户端写数据
清单 10. 服务器端简单实现
// 这里必须设置 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 下通过测试。
相关推荐
1. `postMessage`:这是HTML5引入的一个特性,用于实现跨文档消息传递。通过`window.postMessage`方法,一个窗口可以向任何其他窗口发送消息,无论它们是否属于同一个源。接收端需要监听`message`事件来接收到的消息...
使用方法 `postMessage` 接受两个参数:要传递的数据和目标窗口的引用。例如: ```javascript // 发送方 window.postMessage(messageData, targetOrigin); ``` - `messageData`: 任意类型的数据,可以是字符串、...
5. **HTML5调用Android方法**:在JavaScript中,我们可以通过`Android`对象(由`addJavascriptInterface()`创建)调用Android的方法。需要注意的是,为了安全原因,所有暴露给JavaScript的Java方法都应加上`@...
1. **对于支持HTML5的现代浏览器**(如Chrome、Firefox等),可以使用`postMessage()`方法实现跨域通信。 2. **对于不支持HTML5的老式浏览器**(如IE8),可以通过修改`window.name`属性的方式实现跨域通信。 #### ...
7. **SendMessage与PostMessage的区别**:`SendMessage`是同步的消息发送,它会等待消息被处理完才返回,如果消息处理过程耗时,会阻塞发送线程。而`PostMessage`是异步的,它将消息放入消息队列并立即返回,消息...
4. **消息传递**:对于跨源的`iframe`,可以利用`window.postMessage`方法进行安全的数据通信。`iframe`向其父窗口发送消息,父窗口监听`message`事件来接收并执行相应操作。 以下是一个简单的示例,假设我们有一个...
对于那些不支持`postMessage`方法的旧浏览器(如IE6、IE7),可以使用`window.name`方法来实现跨域通信。`window.name`属性在浏览器的同一窗口(或iframe)的整个生命周期内保持不变,且跨域修改它的值不会触发同源...
如果需要从父页面向子页面发送消息,只需将`window.parent.postMessage()`中的`window.parent`替换为`window`,同时在子页面中使用`window.frames[0]`接收消息。 总之,`postMessage()`是解决跨域通信问题的一个...
5.6.2 sendMessage()和PostMessage() 120 5.7 非标准的消息 121 5.7.1 通知消息 121 5.7.2 VCL内部的消息 122 5.7.3 用户自定义的消息 122 5.8 一个消息系统的剖析:VCL 123 5.9 消息与事件之间的关系 128 5.10 总结...
3. **SendMessage** 和 **PostMessage**:这两个函数用于向窗口发送消息。SendMessage会阻塞直到消息被处理,而PostMessage则会将消息放入队列然后立即返回,适合异步操作。 4. **CreateWindowEx** 和 **...
这个标题"laravel开发-toastr-5-laravel"指的是在Laravel 5版本中集成并使用Toastr进行前端通知的方法。描述中的“Laravel 5的简单toastr通知”意味着我们将探讨如何在Laravel 5项目中轻松实现这个功能。 Toastr库...
学习Windows API,你需要理解如CreateWindow、PostMessage、SendMessage等基本窗口和消息处理函数,以及内存管理、进程线程、系统调用等底层机制。此外,还有GDI(Graphics Device Interface)和DirectX等图形编程...
可以使用`SetWindowLong`和`GetWindowLong` API函数来挂钩窗口过程,或者使用`SendMessage`/`PostMessage` API进行进程间通信(IPC)。 7. **安全和性能考虑**:嵌入外部程序时,应确保程序来源可信,避免潜在的...
7.4.2 postMessage函数 7.4.3 SendMessage函数 7.4.4 SendMessage 7.5 SendNotifymessage和SendMessageCallback 7.6 使用自定义命令 7.7 使用自定义窗口消息 7.8 使用登记消息 7.9 处理线程消息 7.10 跨...
在IT行业中,构建电子邮件应用程序是一项常见的任务,尤其在商业环境中,高效、安全的邮件通信是企业...通过对源码的分析和实践,开发者可以掌握JMail的使用方法,从而在自己的商业项目中实现高效、可靠的邮件功能。
m_pFlashCtrl->PostMessage(WM_COMMAND, ID_SWF_PLAY, 0); ``` 这里 `ID_SWF_PLAY` 是你定义的命令ID,需要在控件的响应函数中处理。 5. **处理Flash事件**:如果你需要对Flash的某些事件作出反应,如点击事件,...
Spring Boot作为Java生态中的轻量级框架,提供了方便快捷地集成WebSocket的方法。本教程将详细介绍如何利用Spring Boot集成WebSocket,实现消息的主动推送,包括一对一的消息传递和广播给所有在线用户。 首先,我们...
transport.sendMessage(message, message.getAllRecipients()); transport.close(); ``` 7. 邮件系统安全考虑: - 使用TLS或SSL加密:SMTP通信应尽可能使用安全套接字层(SSL)或传输层安全(TLS)进行加密,...
例如,`CreateWindowEx`用于创建窗口,`MessageLoop`用于处理消息循环,`SendMessage`和`PostMessage`则用于发送和异步发送消息。理解这些API的使用方法对于编写高效、稳定的Windows程序至关重要。 书中还可能涵盖...
此外,`SendMessage`和`PostMessage`用于向窗口发送消息,而`LoadLibrary`和`GetProcAddress`则用于动态链接库的加载和函数地址获取。 Windows API不仅包含标准的Win32 API,还包括COM(Component Object Model)和...