HTML5 Web Workers可以让WEB应用程序具备后台处理的能力。它对多线程的支持非常好,因此,使用了HTML5的javascript应用程序可以充分利用多核CPU带来的好处。
Web Workers API能够异步和自主的处理javascript文件。一个web worker就是一个处理javascript文件的线程。我们可以将耗时较多的任务分配给HTML5 Web Workers API来处理,这样可以避免弹出脚本运行画面的对话框等等。
虽然Web Workers是否强大,但它也不是万能的,有些事情它也无能为力。例如在Web Workers中执行的脚本不能访问这个页面的window对象(window.document
)。也就是说,Web Workers不能直接访问Web页面和DOM API。虽然Web Workers不会导致浏览器UI停止响应,但是仍然会消耗CPU周期,导致系统响应缓慢。
创建一个Web Worker
在使用Web Workers API之前,要先检查一下浏览器是否支持它。
function isSupportWebWorkers() { if(typeof(worker) != 'undefined') { document.geElementById('support').innerHTML = "你的浏览器支持 HTML5 Web Workers!"; } }
你可以通过下面的代码来创建一个Web Worker:
var worker = new Worker("assets/worker.js");
传入Worker()
方法中的参数是一个要执行的JavaScript文件的URL地址。
和HTML5 Web Workers通信
你可以使用HTML5 Messaging API来和Web Worker进行通信。通过postMessage API可以传送和接收数据。postMessage API还可以跨框架和跨窗口进行通信。大多数的javascript对象都可以通过postMessage进行传送,但是含有循环引用的除外。下面是一段示例代码:
var worker = new Worker("assets/worker.js"); worker.onmessage = function(event) { alert("Reply: " + event.data); } worker.postMessage("Hello worker!");
在上面的代码中,首先创建了一个Worker对象。然后在这个Web Worker上添加了onmessage
事件监听函数,这个函数会在创建它的页面发送信息时被调用。最后,通过worker.postMessage()
方法将信息发送给Web Worker。
Web Worker的响应信息应该类似下面的样子:
this.onmessage = function(event) { postMessage("Reply from web worker"); }
这段代码是Web Worker执行的javascript代码的一部分。this
关键字指向的是Web Worker自己。在这个Web Worker上被添加了一个onmessage
事件监听。它和前面在页面中创建一个Web Worker并添加onmessage
事件监听是不一样的,虽然它们都是在Web Worker对象实例上添加监听事件。Web Worker通过postMessage()
来响应信息。
交互JSON数据
在前面的Web Workers的例子中,浏览器在交互信息时使用的是字符串。如果你想传送JSON对象,你可以使用JSON.stringify()
方法来将一个JSON对象进行编码,然后使用JSON.parse()
方法来进行解码。
在Mozilla MDN上提供了一种新的方法来交换数值或JSON对象,具体请参考:The structured clone algorithm。
完整的Web Workers实现
下面是一段完整的web worker实现代码:
this.onmessage = function(event) { postMessage("Reply from web worker"); } //Implementation of web worker thread code setInterval(function() { runEveryXSeconds() }, 5000); function runEveryXSeconds() { postMessage("Calling back at : " + new Date().getTime()); }
这个web worker实现消息的监听,以及开启动它之后每5秒钟向页面发送一条信息。
Web Worker示例
下面的示例是上面的代码的具体实现。你可以点击下面的按钮来执行这个Web Worker:
要停止Web Worker没5秒钟向页面发送一次信息,可以刷新一次页面。
Web Worker的应用和局限
正如前面所提到的,Web Workers并不是万能的。Web Workers在使用中会有一些限制。
一个Web Worker不能访问创建它的页面的DOM元素。
下面列出了一些可以在Web Worker脚本中执行的操作:
- 使用
onmessage
时间来监听消息。 - 使用
postMessage()
函数来发送消息。 - 使用
XMLHttpRequest
来发送AJAX请求。 - 使用
setTimeout()
和setInterval()
来创建定时器。 - Web Sockets。
- Web SQL Databases
- Web Workers
- 使用
importScripts()
来导入更多的js脚本。
在Web Worker中导入javascript脚本
你可以使用importScripts()
函数来在Web Worker导入javascript脚本来使用。importScripts()
是一个非常特别的Web Worker函数,下面是一个例子:
importScripts("myscript.js"); importScripts("script1.js", "script2.js");
通过importScripts()
函数你可以导入一个或多个js脚本。脚本的加载是同步的,它们会同时被执行。
共享Web Worker
一个Web Worker只能被创建它的页面使用。如果你想在多个页面之间共享一个Web Worker,可以使用SharedWorker
。一个SharedWorker
可以被同源(相同域名)的所有页面访问。
创建SharedWorker
你可以像下面这样创建一个SharedWorker
:
var worker = new SharedWorker("shared-worker.js");
传入SharedWorker
对象的参数是要被执行的js文件的路径字符串。
在所有的页面中都创建一个以相同URL作为参数的SharedWorker
,这将使得它们都连接到同一个SharedWorker
上。
连接一个SharedWorker
在SharedWorker
中有一个概念叫端口,各种引用了SharedWorker
的页面可以通过它联系起来。
下面是一个如何在SharedWorker
端口中监听消息的例子:
var worker = new SharedWorker("/html5/web-worker-shared.jsp"); worker.port.addEventListener("message", function(event) { alert(event.data); } , false ); worker.port.start();
上面的代码首先创建了一个SharedWorker
,然后在这个SharedWorker
的端口上添加事件监听,最后启动这个端口。如果你不启动它,你就不能向SharedWorker
发送消息。
向一个SharedWorker发送消息
当端口被启动之后,你的页面就会在这个端口上监听消息。你可以通过port.postMessage()
函数来向SharedWorker
发送消息。下面是一个示例代码:
worker.port.postMessage("First Message");
一个SharedWorker的完整实现
一个SharedWorker
和普通的Web Worker一样,需要在JavaScript文件中实现它的功能。下面是一个比较完整的实现代码:
var ports = [] ; onconnect = function(event) { var port = event.ports[0]; ports.push(port); port.start(); port.addEventListener("message", function(event) { listenForMessage(event, port); } ); } listenForMessage = function (event, port) { port.postMessage("Reply from SharedWorker to: " + event.data); } //Implementation of shared worker thread code setInterval(function() { runEveryXSeconds() }, 5000); function runEveryXSeconds() { for(i = 0; i < ports.length; i++) { ports[i].postMessage("Calling back at : " + new Date().getTime()); } }
上面的代码首先创建了一个数组来存储所有连接SharedWorker
的页面的端口。
接着定义了一个onconnect
函数,这个函数会在一个页面和SharedWorker
连接时被调用。
onconnect
函数先获得连接页面的端口,并将它存储在端口数组中,然后启动端口。最后,onconnect
函数在端口上添加一个消息监听。这里要注意他为每一个连接页面创建了一个匿名函数。这个匿名函数会捕捉连接页面的端口,并将它作为参数传入到listenForMessage()
函数中。
接下来定义了一个listenForMessage()
函数,这个函数使用一串简单的字符串来响应接收到消息的端口。
最后,在SharedWorker
中设置了一个定时器,每5秒钟执行一次runEveryXSeconds()
函数。
runEveryXSeconds()
函数简单的迭代所有的连接端口,并向它们发生一条消息。
上面的代码只是一个最简单的实现。一个高级的实现可以从服务器上获取数据,并将数据分布到所有连接到SharedWorker
的页面中。
一个SharedWorker例子
下面是一个SharedWorker
的例子。你可以点击按钮来启动SharedWorker
。然后通过右边的按钮来向SharedWorker
发送信息。