介绍
Web Workers允许你在后台运行JavaScript代码,而不会阻止web用户界面。Web Workers可以提高网页的整体性能,还可以增强用户体验。Web Workers有两种风格 ——专用Web Workers和共享Web Workers。本文讨论了你所需要知道的Web worker的七个关键方面,帮助你决定在应用程序中使用它们的话。
1.Web Workers允许你在后台运行JavaScript代码
通常,你在Web页面中编写的JavaScript代码在与用户界面相同的线程中执行。这就是为什么当你点击一个会触发漫长处理过程的按钮,网页的用户界面会冻结。除非处理完成,否则你就无法工作于用户界面。Web worker允许你在后台执行JavaScript,以便用户界面保持响应,即使同时正在执行某些脚本。执行脚本的后台线程通常称为worker thread或worker。你可以生成尽可能多的worker,只要你想。你还可以将数据传递到正在worker thread中执行的脚本,并在完成时将值返回到主线程。然而,Web Workers有一些限制,如下所示:
- Web Workers无法从web页面访问DOM元素。
- Web Workers无法从web页面访问全局变量和JavaScript函数。
- Web Workers不能调用alert()或confirm()函数。
- 不能在Web Workers中访问诸如窗口,文档和parent这样的对象。
但是,你可以使用setTimeout(),setInterval()等函数。你也可以使用XMLHttpRequest对象向服务器发出Ajax请求。
2.Web Workers有两种类型
Web Workers有两种类型:专用Web Workers和共享Web Workers。专用Web Workers随同创建它们的网页一起存在和死亡。这意味着在网页中创建的专用Web Workers无法通过多个网页访问。另一方面,共享Web Workers在多个网页之间是共享的。Worker类代表专用Web Workers,而SharedWorker类代表共享Web Workers。
在许多情况下,专用Web Workers就可以满足你的需求。这是因为通常你需要在worker thread中执行一个网页的特定脚本。然而,有时,你需要在worker thread中执行一个脚本,并且这个worker thread对多个网页通用。在这种情况下,创建许多专用Web Workers,每个页面一个,还不如使用共享Web Workers。由一个网页创建的共享web worker仍然可用于其他网页。只有当所有到它的连接被关闭,才能毁坏它。共享Web Workers比专用Web Workers更复杂一点。
3.Worker对象代表专用Web Worker
现在,你了解了Web Workers的基础知识,让我们看看如何使用专用Web Workers。下面讨论的示例假设你已使用喜欢的开发工具创建了一个Web应用程序,并且还在其Script文件夹中添加了jQuery和Modernizr库。将HTML页面添加到web应用程序,然后键入以下代码:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script src="scripts/modernizr.js"></script>
<script src="scripts/jquery-2.0.0.js"></script>
<script type="text/javascript"> $(document).ready(function () { if (!Modernizr.webworkers) { alert("This browser doesn't support Web Workers!"); return; } $("#btnStart").click(function () { var worker = new Worker("scripts/lengthytask.js"); worker.addEventListener("message", function (evt) { alert(evt.data); },false); worker.postMessage(10000); }); }); </script>
</head>
<body>
<form>
<input type="button" id="btnStart" value="Start Processing" />
</form>
</body>
</html>
上面的HTML页面包含一个触发一些JavaScript处理的按钮(btnStart)。请注意,该网页引用了Modernizr和jQuery库。<script>块包括ready()方法处理程序,并且该处理程序又反过来处理btnStart的单击事件。ready()处理程序首先检查浏览器是否支持web workers。这通过使用Modernizr的webworkers属性完成。如果浏览器不支持Web workers,则会向用户显示一条错误消息。
然后代码连接btnStart的点击事件处理程序。点击事件处理程序的代码很重要,因为它使用Worker对象在后台运行脚本。点击事件处理程序创建一个Worker对象并将其存储在本地变量——worker中。要在后台执行的JavaScript文件的路径在构造函数中传递。你将很快创建LengthyTask.js。然后,代码为Worker对象的消息事件添加一个事件处理程序。当目标脚本(在此情况下为LengthyTask.js)将一些值发送回网页时,会引发消息事件。消息事件处理函数可以使用evt.data属性来访问返回的值。***,在Worker对象上调用postMessage()方法来触发LengthyTask.js的执行。 postMessage()方法还允许你将数据传递到目标脚本。在此示例中,将一个数字(10000)传递给postMessage(),postMessage()指示处理应持续的毫秒数量。你可以传递postMessage()调用中的任何其他数据,如JavaScript对象或字符串。
LengthyTask.js文件包含要在后台执行的代码,如下所示:
addEventListener("message", function (evt) {
var date = new Date();
var currentDate = null;
do {
currentDate = new Date();
} while (currentDate - date < evt.data);
postMessage(currentDate);
}, false);
上面的代码处理worker thread的消息事件。当主页面调用Worker对象上的postMessage()方法时,会引发消息事件。消息事件处理程序通过运行某些毫秒的do-while循环来模拟冗长的处理。此循环运行的毫秒数从主页传递(回忆前面讨论的postMessage())。因此,evt.data在此示例中返回10000。一旦长时间操作完成,代码调用postMessage()会把处理结果发送回主页面。在本例中,传递currentDate的值(currentDate是一个Date对象)。
如果你运行主网页并单击Start Processing按钮,那么你将在10秒后收到alert()。同时,页面的用户界面不会被阻止,你可以执行诸如滚动,点击等操作,表明来自LengthyTask.js的代码正在后台运行。
4. SharedWorker对象代表共享Web Worker
前面的示例使用了专用Web worker。让我们将同样的示例转换为使用共享Web worker。共享Web worker由SharedWorker对象表示。下面的代码显示了来自主页的代码的修改版本:
$(document).ready(function () {
if (!Modernizr.webworkers)
{
alert("This browser doesn't support Web Workers!");
return;
}
$("#btnStart").click(function () {
var worker = new SharedWorker("scripts/sharedlengthytask.js"); worker.port.addEventListener("message", function (evt) { alert(evt.data); }, false); worker.port.start(); worker.port.postMessage(10000); });
});
注意用粗体字标记的代码。它创建了一个SharedWorker实例,并在构造函数中传递SharedLengthyTask.js。你将很快创建此文件。然后,代码将消息事件处理程序连接到SharedWorker对象的端口对象。消息处理程序函数执行与前面示例中相同的工作。然后代码在端口对象上调用start()方法。***,在端口对象上调用postMessage()方法将数据(10000)发送到共享worker thread。
SharedLengthyTask.js文件包含以下代码:
var port;
addEventListener("connect", function (evt) {
port = evt.ports[0];
port.addEventListener("message", function (evt) {
var date = new Date();
var currentDate = null;
do {
currentDate = new Date();
} while (currentDate - date < evt.data);
port.postMessage(currentDate);
}, false);
port.start();
}, false);
代码首先声明一个名为port的变量,用于存储端口对象的引用。这次处理了两个事件——connect和message。当与共享Web worker建立连接时,会触发connect事件。 connect事件处理程序捕获evt.port [0]对象并将其存储在之前声明的端口变量中。然后在端口对象上连接消息事件处理程序。调用端口对象的start()方法来开始侦听该端口上的消息。消息事件处理程序几乎与你在前面的示例中编写的消息事件处理程序相同,除了它附加到端口对象这一点。此外,在端口对象上调用postMessage(),以将处理结果发送到主页面。
5. Web Workers可以使用XMLHttpRequest与服务器通信
有时Web Worker可能需要与Web服务器通信。例如,你可能需要驻留在某些RDBMS中的数据以便于客户端处理。要完成此类任务,你可以使用XMLHttpRequest对象向服务器端资源发出请求。实例化Worker对象和处理消息事件的整个过程保持不变。但是,你需要向服务器端资源发出GET或POST请求。考虑下面的代码:
addEventListener("message", function (evt) {
var xhr = new XMLHttpRequest(); xhr.open("GET", "lengthytaskhandler.ashx"); xhr.onload = function () { postMessage(xhr.responseText); }; xhr.send(); }, false);
上面显示的代码创建了XMLHttpRequest对象的实例。然后调用open()方法,并指定向服务器端资源LengthyTaskHandler.ashx(一个ASP.NET通用处理程序)发出GET请求。(虽然此示例使用ASP.NET通用处理程序,但你可以使用任何其他服务器端资源。)然后它处理XMLHttpRequest对象的load事件并调用postMessage()。 xhr.responseText作为postMessage()的参数。xhr.responseText将是ASP.NET通用处理程序作为响应返回的值。请求完成时引发load事件。
LengthyTaskHandler.ashx包含以下代码:
namespace WebWorkersDemo
{
public class LengthyTaskHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context) { System.Threading.Thread.Sleep(10000); context.Response.ContentType = "text/plain"; context.Response.Write("Processing successful!"); }
public bool IsReusable
{
get
{
return false;
}
}
}
}
正如你可以看到,ProcessRequest()通过在Thread类上调用Sleep()方法来模拟一些冗长的处理,并阻止执行10秒。然后它返回一个成功消息“Processing successful!”给调用者。如果你在进行这些更改后运行主网页,你会发现在10秒后,将显示一个包含此成功消息的警报对话框。
6.你可以使用错误事件捕获未处理的错误
如果你的Web worker正在进行一些复杂的操作,那么你可能需要添加错误处理到主网页代码,以便在worker中出现任何未处理错误时,可以采取适当的操作。这可以通过处理Worker对象的错误事件来完成。每当work thread中存在任何未处理的错误时,就会抛出错误事件。以下代码显示了如何完成此操作:
$("#btnStart").click(function () {
var worker = new Worker("scripts/lengthytask.js");
worker.addEventListener("message", function (evt) {
alert(evt.data);
}, false);
worker.addEventListener("error", function (evt) { alert("Line #" + evt.lineno + " - " + evt.message + " in " + evt.filename); }, false); worker.postMessage(10000);
});
从上面的代码可以看出,错误处理程序已经连接到worker对象的错误事件。错误处理函数接收一个事件对象,而该对象提供错误信息,例如发生错误的行号(evt.lineno),错误消息(evt.message)和发生错误的文件(evt.filename)。
7.你可以使用Terminate()方法终止worker
有时你可能会想要取消worker中正在执行的任务。对此,你可以通过调用其terminate()方法来摧毁Worker。一旦Worker终止,你就不能重新使用或重新启动它。当然,你总是可以创建另一个Worker实例并使用它。但请记住,terminate()会立即杀死了worker,并且不会给你任何机执行清理操作。
总结
Web workers允许你在后台执行脚本而不冻结网页用户界面。有两种类型——专用web worker和共享web worker。每个网页创建专用web worker,而跨多个网页使用共享web worker共享。Worker类代表专用web worker,SharedWorker类代表共享web worker。本文介绍了如何使用这两种类型,文中还讨论了如何处理错误以及webworker如何使用XMLHttpRequest与web服务器通信。
译文链接:http://www.codeceo.com/article/7-things-about-web-workers.html
英文原文:7 Things You Need To Know About Web Workers