web workers
随着Ajax和Web 2.0应用程序的问世,最终用户对Web应用程序的快速响应感到迷恋。 在Web应用程序可以更快地响应之前,必须消除某些瓶颈。 瓶颈包括JavaScript和后台I / O的大量计算,需要从主UI呈现过程中删除这些计算。 输入网络工作者。
Web Workers规范提供了独立于任何用户界面脚本在后台运行脚本的功能。 长时间运行的脚本不会因响应点击或其他用户交互的脚本而中断。 Web Worker允许执行长任务,而不会让步,同时保持页面响应速度。
在Web Workers之前,JavaScript是现代Web应用程序的核心。 JavaScript和与之交谈的DOM本质上是单线程的:在任何给定时间只能执行一个JavaScript方法。 即使您的计算机有四个核心,在进行长时间计算时,它也只会使其中一个核心处于繁忙状态。 例如,如果您计算到达月球的理想轨迹,则您的浏览器将无法渲染显示轨迹的动画,并且无法对用户事件(例如鼠标单击或键盘键入)做出React。 。
Web Workers打破了传统JavaScript单线程模式,并引入了多线程编程模型。 工作程序是独立线程。 具有许多任务要处理的Web应用程序不再需要一一处理任务。 而是,应用程序可以将任务分配给不同的工作人员。
在本文中,了解Web Workers API。 一个实际的示例将引导您完成使用Web Workers快速呈现网页的步骤。
从下面的下载表中下载本文中使用的示例的源代码。
Web Workers的基本组件是:
一个工作人员可以完成很多工作,包括并行计算,后台I / O和客户端数据库操作。 工作人员不应中断主UI或直接操作DOM; 它应该向主线程返回一条消息,并让主线程更新主UI。
本节介绍Web Workers API的基础。
要创建新的工作程序,只需将工作程序脚本URI作为唯一参数调用工作程序构造函数。 创建工作程序后,将同时启动一个新线程(或可能取决于浏览器的实现的新进程)。
当工作人员完成工作或遇到错误时,您可以从工作人员处onmessage
带有工作实例的onmessage
和onerror
属性的通知。 清单1显示了一个样例工作程序。
// receive a message from the main JavaScript thread
onmessage = function(event) {
// do something in this worker
var info = event.data;
postMessage(info + “ from worker!”);
};
如果运行下面的清单2中JavaScript代码,您将得到结果“来自工作人员的Hello World”。
// create a new worker
var myWorker = new Worker("myWorker.js");
// send a message to start the worker
var info = “Hello World”;
myWorker.postMessage(info);
// receive a message from the worker
myWorker.onmessage = function (event) {
// do something when receiving a message from worker
alert(event.data);
};
工作者是一个线程(或进程,本质上),它是一个资源消耗很高的OS级对象。 当分配给工作程序的任务完成时,或者您只想terminate
它,您需要调用工作程序的terminate
方法来终止正在运行的工作程序。 工作线程或进程将立即被杀死,而没有机会完成其操作或清理自身。 清单3显示了一个示例。
myWorker.terminate();
与典型JavaScript代码类似,正在运行的工作程序中可能会发生运行时错误。 要处理错误,您需要为工作程序设置onerror
处理程序,如果在工作程序中运行脚本期间发生任何错误,则将调用该处理程序。 该事件不会冒泡,您可以取消它。 为了防止发生默认操作,工作程序可以调用错误事件的preventDefault()
方法。
myWorker.onerror = function(event){
console.log(event.message);
console.log(event.filename);
console.log(event.lineno);
}
错误事件具有以下三个字段,可能有助于调试:
message
:人类可读的错误消息 filename
:发生错误的脚本文件的名称 lineno
:发生错误的脚本文件行号 importScripts()
线程可以访问全局函数importScripts()
,该函数使它们可以将脚本或库导入其作用域。 它接受零个或多个要导入的资源URI作为参数。
//import nothing
importScripts();
//import just graph.js
importScripts('graph.js');
//import two scripts
importScripts('graph.js', 'controller.js');
本节将向您介绍Web Workers的实际使用案例。 该示例涉及呈现一个包含几个基于Dojo的Website Displayer小部件的页面。 这些小部件用于通过iFrame显示网站。 如果没有Web Workers,则必须通过Ajax请求获取小部件定义,然后在单个JavaScript线程中呈现它们。 如果小部件定义包含大量数据,这将非常慢。
该示例创建了一些工作程序来获取窗口小部件定义。 每个工作人员都被分配了一个任务,以获取一个小部件定义,并负责告诉主UI JavaScript线程进行渲染。 因为工人可以并行工作,所以这是一个更快的解决方案。
本示例使用Dojo 1.4。 如果你想在自己的浏览器中运行的例子,下载Dojo库(见相关主题 )和源代码(见下载的资源 ),在这篇文章中使用。 图1显示了示例应用程序的结构。
在图1中:
XMLHttpRequst
的方法。 网站显示器小部件是一个非常简单的基于Dojo-TitlePane-dijit的小部件。 它将呈现形式化标题窗格的UI,如图2所示。
清单6显示了WebsiteDisplayer.js的代码。
dojo.require("dijit._Widget");
dojo.require("dijit._Templated");
dojo.require("dijit.TitlePane");
dojo.declare("loadWidget.WebsiteDisplayer", [dijit.TitlePane], {
title: "",
url: "",
postCreate: function() {
var ifrm = dojo.create("iframe", {
src: this.url,
style: "width:100%;height:20%;"
});
dojo.place(ifrm, this.domNode.children[1], "first");
this.inherited(arguments);
var contentFrame = this.domNode.children[1].children[0];
if (contentFrame.attachEvent) {
contentFrame.attachEvent("onload",
function() {
dojo.publish("frameEvent/loaded");
}
);
} else {
contentFrame.onload = function() {
dojo.publish("frameEvent/loaded");
};
}
}
});
要实现worker.js,请导入一个名为XMLHttpRequest.js的全局JavaScript文件,其中包含全局方法creatXMLHTTPRequest
。 此方法将返回XMLHttpRequest
对象。
工作人员将首先将XMLHttpRequest
发送到服务器端,并将小部件定义检索回到主JavaScript线程。 清单7和8显示了一个示例。
importScripts("XMLHttpRequest.js");
onmessage = function(event) {
var xhr = creatXMLHTTPRequest();
xhr.open('GET', 'widgets/widgetDefinition' + event.data + '.xml', true);
xhr.send(null);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200 || xhr.status ==0) {
postMessage(xhr.responseText);
} else {
throw xhr.status + xhr.responseText;
}
}
}
}
在主页上,您可以:创建多个工作程序; 向工人发送消息并启动工人; 接收工人的信息; 并使用收到的消息来操作主界面。
Load widgets with Web Workers
Widgets are loading......
将该主页嵌入到Web应用程序中并运行它。 结果应如图3所示。
若要查看使用Web Workers与不使用Web Workers之间的区别,请分别运行LoadWidget.html和LoadWidget-none-web-workers.html并查看结果。 请注意,由于代码示例处理的数据很少,因此没有Web Workers的页面的运行速度将比具有Web Workers的页面更快。 节省的时间与开始工作的成本相平衡。
前面的示例涉及XMLHttpRequest
和计算。 它不是很大或复杂。 当您给工作人员执行更复杂的任务(例如处理大型计算)时,它将成为一项非常强大的功能。 在项目中采用这项很酷的技术之前,请阅读以下提示。
出于安全原因,工作人员无法直接操作HTML文档。 在同一个DOM上运行的多个线程会在线程安全性方面造成问题。 优点是您不再需要担心工作程序实现中的多线程安全问题。
在培养工人时,这种情况有一些限制。 您不能在worker中调用alert()
,这是调试JavaScript代码的一种非常流行的方法。 您也不能调用document.getElementById()
,因为它只能接收和返回变量(可以是字符串,数组,JSON对象,等等)。
尽管工作人员无法访问window
对象,但它可以直接访问navigator
。 您可以在navigator
对象中访问appName
, appVersion
, platform
和userAgent
。
可以以只读方式访问location
对象。 您可以在location
对象内部获取hostname
和port
。
如本文示例中所示,还可以在工作程序中启用XMLHttpRequest
。 由于此功能,您可以在worker中添加许多有趣的扩展。
也可用:
importScripts()
方法(用于访问同一域中的脚本文件) Object
, Array
, Date
, Math
和String
setTimeout()
和setInterval()
方法 postMessage
携带的数据类型 postMessage
被大量使用,因为它是主要JavaScript线程与工作人员进行交互的主要方法。 但是,现在可以在postMessage
中携带的数据类型仅限于本机JavaScript类型,例如Array,Date,Math,String,JSON等。 对复杂的自定义JavaScript对象的支持不是很好。
翻译自: https://www.ibm.com/developerworks/web/library/wa-webworkers/index.html
web workers