9159金沙游艺场

图片 42
移动端布局解决方案
9159金沙游艺场 5
.NET 任务调度Quartz系列(1)——自建定时任务

开始使用Web Workers

开始使用Web Workers

2012/11/28 · HTML5,
JavaScript · 来源:
伯乐在线     ·
HTML5,
Javascript

英文原文:tutsplus,编译:伯乐在线
– 胡蓉(@蓉Flora)

单线程(Single-threaded)运行是JavaScript语言的设计目标之一,进而言之是保持JavaScript的简单。但是我必须要说,尽管JavaScript具有如此语言特质,但它绝不简单!我们所说的“单线程”是指JavaScript只有一个线程控制。是的,这点令人沮丧,JavaScript引擎一次只能做一件事。

“web workers处在一个严格的无DOM访问的环境里,因为DOM是非线程安全的。”

现在,你是不是觉得要想利用下你机器闲置的多核处理器太受限制?不用担心,HTML5将改变这一切。

JavaScript的单线程模式

有学派认为JavaScript的单线程特质是一种简化,然而也有人认为这是一种限制。后者提出的是一个很好的观点,尤其是现在web应用程序大量的使用JavaScript来处理界面事件、轮询服务端接口、处理大量的数据以及基于服务端的响应操作DOM。

在维护响应式界面的同时,通过单线程控制处理如此多事件是项艰巨的任务。它迫使开发人员不得不依靠一些技巧或采用变通的方法(如使用setTimeout(),setInterval(),或调用XMLHttpRequest和DOM事件)来实现并发。然而,尽管这些技巧毫无疑问地提供了解决异步调用的方法,但非阻塞的并不意味着是并发的。John
Resig在他的博客中解释了为什么不能并行运行。

限制

如果你已经和JavaScript打过一段时间的交道,那么你一定也遭遇过如下令人讨厌的对话框,提示你有脚本无响应。没错,几乎大多数的页面无响应都是由JavaScript代码引起的。

图片 1

以下是一些运行脚本时造成浏览器无响应的原因:

  • 过多的DOM操作:DOM操作也许是在JavaScript运行中代价最高的。所以,大批量的DOM操作无疑是你代码重构的最佳方向之一。
  • 无终止循环:审视你代码中复杂的嵌套循环永远不是坏事。复杂的嵌套循环所做的工作常常比实际需要做的多很多,也许你可以找到其他方法来实现同样的功能。
  • 同时包含以上两种:最坏的情况就是明明有更优雅的办法,却还是在循环中不断更新DOM元素,比如可以采用DocumentFragment。

 

点评:HTML5 中的 Web Worker 可以分为两种不同线程类型,一个是专用线程
Dedicated Worker,一个是共享线程 Shared
Worker。两种类型的线程各有不同的用途,感兴趣的朋友可以了解下啊,或许对你有所帮助

Javascript执行机制       在HTML5之前,浏览器中JavaScript的运行都是以单线程的方式工作的,虽然有多种方式实现了对多线程的模拟(例如:Javascript
中的 setinterval 方法,setTimeout
方法等),但是在本质上程序的运行仍然是由 JavaScript
引擎以单线程调度的方式进行的。在 HTML5 中引入的工作线程使得浏览器端的
Javascript 引擎可以并发地执行 Javascript
代码,从而实现了对浏览器端多线程编程的良好支持。

好帮手Web Workers

幸好有了HTML5和Web
Workers,你可以真正生成一条异步的线程。当主线程处理界面事件时,新的worker可以在后台运行,它甚至可以有力的处理大量的数据。例如,一个worker可以处理大型的数据结构(如JSON),从中提取变量信息然后在界面中显示。好了,废话不多说,让我们看一些实际的代码吧。

 

创建一个Worker

通常,与web
worker相关的代码都放在一个独立的JavaScript文件中。父线程通过在Worker构造函数中指定一个JavaScript文件的链接来创建一个新的worker,它会异步加载并执行这个JavaScript文件。

JavaScript

var primeWorker = new Worker(‘prime.js’);

1
var primeWorker = new Worker(‘prime.js’);

 

启动Worker

要启动一个Worker,则父线程向worker传递一个信息,如下所示:

JavaScript

var current = $(‘#prime’).attr(‘value’);
primeWorker.postMessage(current);

1
2
var current = $(‘#prime’).attr(‘value’);
primeWorker.postMessage(current);

父页面可以通过postMessage接口与worker进行通信,这也是跨源通信(cross-origin
messaging)的一种方式。通过postMessage接口除了可以向worker传递私有数据类型,它还支持JSON数据结构。但是,你不能传递函数,因为函数也许会包含对潜在DOM的引用。

“父线程和worker线程有它们各自的独立空间,信息主要是来回交换而不是共享。”

信息在后台运行时,先在worker端序列化,然后在接收端反序列化。鉴于此,不推荐向worker发送大量的数据。

父线程同样可以声明一个回调函数,来侦听worker完成任务后发回的消息。这样,父线程就可以在worker完成任务后采取些必要的行动,比如更新DOM元素。如下代码所示:

JavaScript

primeWorker.addEventListener(‘message’, function(event){
console.log(‘Receiving from Worker: ‘+event.data); $(‘#prime’).html(
event.data ); });

1
2
3
4
primeWorker.addEventListener(‘message’, function(event){
    console.log(‘Receiving from Worker: ‘+event.data);
    $(‘#prime’).html( event.data );
});

event对象包含两个重要属性:

  • target:用来指向发送信息的worker,在多元worker环境下比较有用。
  • data:由worker发回给父线程的数据。

worker本身是包含在prime.js文件中的,它同时侦听message事件,从父线程中接收信息。它同样通过postMessage接口与父线程进行通信。

JavaScript

self.addEventListener(‘message’, function(event){ var currPrime =
event.data, nextPrime; setInterval( function(){ nextPrime =
getNextPrime(currPrime); postMessage(nextPrime); currPrime = nextPrime;
}, 500); });

1
2
3
4
5
6
7
8
self.addEventListener(‘message’,  function(event){
    var currPrime = event.data, nextPrime;
    setInterval( function(){
    nextPrime = getNextPrime(currPrime);
    postMessage(nextPrime);
    currPrime = nextPrime;
    }, 500);
});

在本文例子中,我们寻找下一个最大的质数,然后不断将结果发回至父线程,同时不断更新界面以显示新的值。在worker的代码中,字段self和this都是指向全局作用域。Worker既可以添加事件侦听器来侦听message事件,也可以定义一个onmessage处理器,来接收从父线程发回的消息。

寻找下一个质数的例子显然不是worker的理想用例,但是在此选用这个例子是为了说明消息传递的原理。之后,我们会挖掘些可以通过web
worker获得益处的实际用例。

 

终止Workers

worker属于占用资源密集型,它们属于系统层面的线程。因此,你应该不希望创建太多的worker线程,所以你需要在它完成任务后终止它。Worker可以通过如下方式由自己终止:

JavaScript

self.close();

1
self.close();

或者,由父线程终止。

JavaScript

primeWorker.terminate();

1
primeWorker.terminate();

 

安全与限制

在worker的代码中,不要访问一些重要的JavaScript对象,如document、window、console、parent,更重要的是不要访问DOM对象。也许不用DOM元素以至不能更新页面元素听上去有点严格,但是这是一个重要的安全设计决定。

想象一下,如果众多线程都试着去更新同一个元素那就是个灾难。所以,web
worker需要处在一个严格的并线程安全的环境中。

正如之前所说,你可以通过worker处理数据,并将结果返回主线程,进而更新DOM元素。尽管它们不能访问一些重要的JavaScript对象,但是它们可以调用一些函数,如setTimeout()/clearTimeout()、setInterval()/clearInterval()、navigator等等,也可以访问XMLHttpRequest和localStorge对象。

 

同源限制

为了能和服务器交互,worker必须遵守同源策略(same-origin policy)(译注:可参考国人文章同源策略)。比如,位于

 

Google Chrome与本地访问

Google
Chrome对worker本地访问做了限制,因此你无法本地运行这些例子。如果你又想用Chrome,那么你可以将文件放到服务器上,或者在通过命令启动Chrome时加上–allow-file-access-from-files。例如,苹果系统下:

$ /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
–allow-file-access-from-files

然而,在实际产品生产过程中,此方法并不推荐。最好还是将你的文件上传至服务器中,同时进行跨浏览器测试。

 

Worker调试和错误处理

不能访问console似乎有点不方便,但幸亏有了Chrome开发者工具,你可以像调试其他JavaScript代码那样调试worker。

图片 2

为处理web
worker抛出的异常,你可以侦听error事件,它属于ErrorEvent对象。检测该对象从中了解引起错误的详细信息。

JavaScript

primeWorker.addEventListener(‘error’, function(error){ console.log(‘
Error Caused by worker: ‘+error.filename + ‘ at line number:
‘+error.lineno + ‘ Detailed Message: ‘+error.message); });

1
2
3
4
5
primeWorker.addEventListener(‘error’, function(error){
    console.log(‘ Error Caused by worker: ‘+error.filename
        + ‘ at line number: ‘+error.lineno
        + ‘ Detailed Message: ‘+error.message);
});

多个Worker线程

尽管创建多个worker来协调任务分配也许很常见,但还是要提醒一下各位,官方规范指出worker属于相对重量级并能长期运行在后台的脚本。所以,由于Web
worker的高启动性能成本和高进程内存成本,它们的数量不宜过多。

 

简单介绍共享workers

官方规范指出有两种worker:专用线程(dedicated worker)和共享线程(shared
worker)。到目前为止,我们只列举了专用线程的例子。专用线程与创建线程的脚本或页面直接关联,即有着一对一的联系。而共享线程允许线程在同源中的多个页面间进行共享,例如:同源中所有页面或脚本可以与同一个共享线程通信。

“创建一个共享线程,直接将脚本的URL或worker的名字传入SharedWorker构造函数”

两者最主要的区别在于,共享worker与端口相关联,以保证父脚本或页面可以访问。如下代码创建了一个共享worker,并声明了一个回调函数以侦听worker发回的消息
,同时向共享worker传输一条消息。

JavaScript

var sharedWorker = new SharedWorker(‘findPrime.js’);
sharedWorker.port.onmessage = function(event){ … }
sharedWorker.port.postMessage(‘data you want to send’);

1
2
3
4
5
var sharedWorker = new SharedWorker(‘findPrime.js’);
sharedWorker.port.onmessage = function(event){
    …
}
sharedWorker.port.postMessage(‘data you want to send’);

同样,worker可以侦听connect事件,当有客户端想与worker进行连接时会相应地向其发送消息。

JavaScript

onconnect = function(event) { // event.source包含对客户端端口的引用 var
clientPort = event.source; // 侦听该客户端发来的消息
clientPort.onmessage = function(event) { //
event.data包含客户端发来的消息 var data = event.data; …. //
处理完成后发出消息 clientPort.postMessage(‘processed data’); } };

1
2
3
4
5
6
7
8
9
10
11
12
onconnect = function(event) {
    // event.source包含对客户端端口的引用
    var clientPort = event.source;
    // 侦听该客户端发来的消息
    clientPort.onmessage = function(event) {
        // event.data包含客户端发来的消息
        var data = event.data;
        ….
        // 处理完成后发出消息
        clientPort.postMessage(‘processed data’);
    }
};

由于它们具有共享的属性,你可以保持一个应用程序在不同窗口内的相同状态,并且不同窗口的页面通过同一共享worker脚本保持和报告状态。想更多的了解共享worker,我建议你阅读官方文档。

 

实际应用场景

worker的实际发生场景可能是,你需要处理一个同步的第三方接口,于是主线程需要等待结果再进行下一步操作。这种情况下,你可以生成一个worker,由它代理,异步完成此任务。

Web
worker在轮询情况下也非常适用,你可以在后台不断查询目标,并在有新数据时向主线程发送消息。

你也许遇到需要向服务端返回大量的数据的情况。通常,处理大量数据会消极影响程序的响应能力,然后导致不良用户体验。更优雅的办法是将处理工作分配给若干worker,由它们处理不重叠的数据。

还有应用场景会出现在通过多个web
worker分析音频或视频的来源,每个worker针对专项问题。

 

结论

随着HTML5的展开,web worker规范也会持续加入。如果你打算使用web
worker,看一看它的官方文档不是坏事。

专项线程的跨浏览器支持目前还不错,Chrome,Safari和Firefox目前的版本都支持,甚至IE这次都没有落后太多,IE10还是不错的。但是共享线程只有当前版本的Chrome和Safari支持。另外奇怪的一点是,Android
2.1的浏览器支持web worker,反而4.0版本不支持。苹果也从iOS 5.0开始支持web
worker。

想象一下,在原本单线程环境下,多线程会带来无限可能哦~

 

译注:本人对此JavaScript技术领域并不是特别熟悉,如有误翻的地方,请大家及时批评指正,我将及时修改!!!最后,推荐两篇相关国人优秀文章

《HTML5 web worker的使用 》

《深入HTML5 Web
Worker应用实践:多线程编程》

 

 

英文原文:tutsplus,编译:伯乐在线
– 胡蓉(@蓉Flora)

文章链接:

【如需转载,请在正文中标注并保留原文链接、译文链接和译者等信息,谢谢合作!】

 

赞 1 收藏
评论

Javascript执行机制
在HTML5之前,浏览器中JavaScript的运行都是以单线程的方式工作的,虽然有多种方式实现了对多线程的模拟(例如:Javascript
中的 setinterval 方法,setTimeout
方法等),但是在本质上程序的运行仍然是由 JavaScript
引擎以单线程调度的方式进行的。在 HTML5 中引入的工作线程使得浏览器端的
Javascript 引擎可以并发地执行 Javascript
代码,从而实现了对浏览器端多线程编程的良好支持。

Javascript中的多线程 –
WebWorker
      HTML5 中的 Web Worker 可以分为两种不同线程类型,一个是专用线程
Dedicated Worker,一个是共享线程 Shared
Worker。两种类型的线程各有不同的用途。

关于作者:胡蓉

图片 3

胡蓉:某互联网公司交互设计师。在这么一个梦想者云集的互联网乐土中,用心培育着属于自己的那一片天地。做自己热爱的,然后一直坚持下去~(新浪微博:@蓉Flora)

个人主页 ·
我的文章

图片 4

Javascript中的多线程 – WebWorker HTML5 中的 Web Worker 可以分为两种不同线程类型,一个是专用线程
Dedicated Worker,一个是共享线程 Shared
Worker。两种类型的线程各有不同的用途。
专用型web worker
专用型worker与创建它的脚本连接在一起,它可以与其他的worker或是浏览器组件通信,但是他不能与DOM通信。专用的含义,我想就是这个线程一次只处理一个需求。专用线程在除了IE外的各种主流浏览器中都实现了,可以放心使用。
创建线程
创建worker很简单,只要把需要在线程中执行的JavaScript文件的文件名传给构造函数就可以了。
线程通信
在主线程与子线程间进行通信,使用的是线程对象的postMessage和onmessage方法。不管是谁向谁发数据,发送发使用的都是postMessage方法,接收方都是使用onmessage方法接收数据。postMessage只有一个参数,那就是传递的数据,onmessage也只有一个参数,假设为event,则通过event.data获取收到的数据。
发送JSON数据
JSON是JS原生支持的东西,不用白不用,复杂的数据就用JSON传送吧。例如:

专用型web worker

代码如下:

  专用型worker与创建它的脚本连接在一起,它可以与其他的worker或是浏览器组件通信,但是他不能与DOM通信。专用的含义,就是这个线程一次只处理一个需求。专用线程在除了IE外的各种主流浏览器中都实现了,可以放心使用。
创建线程
      创建worker很简单,只要把需要在线程中执行的JavaScript文件的文件名传给构造函数就可以了。

postMessage({‘cmd’: ‘init’, ‘timestamp’: Date.now()});

线程通信
      在主线程与子线程间进行通信,使用的是线程对象的postMessage和onmessage方法。不管是谁向谁发数据,发送发使用的都是postMessage方法,接收方都是使用onmessage方法接收数据。postMessage只有一个参数,那就是传递的数据,onmessage也只有一个参数,假设为event,则通过event.data获取收到的数据。

处理错误
当线程发生错误的时候,它的onerror事件回调会被调用。所以处理错误的方式很简单,就是挂接线程实例的onerror事件。这个回调函数有一个参数error,这个参数有3个字段:message

发送JSON数据
      JSON是JS原生支持的东西,不用白不用,复杂的数据就用JSON传送吧。例如:

  • 错误消息;filename – 发生错误的脚本文件;lineno – 发生错误的行。
    销毁线程
    在线程内部,使用close方法线程自己销毁自己。在线程外部的主线程中,使用线程实例的terminate方法销毁线程。
    下面从一个例子看线程的基本操作:
    HTML代码:

postMessage({‘cmd’: ‘init’, ‘timestamp’: Date.now()});

代码如下:

处理错误
      当线程发生错误的时候,它的onerror事件回调会被调用。所以处理错误的方式很简单,就是挂接线程实例的onerror事件。这个回调函数有一个参数error,这个参数有3个字段:message

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″
/>
<title>web worker fibonacci</title>
<script type=”text/javascript”>
onload = function(){
var worker = new Worker(‘fibonacci.js’);
worker.onmessage = function(event) {
console.log(“Result:” + event.data);
};
worker.onerror = function(error) {
console.log(“Error:” + error.message);
};
worker.postMessage(40);
}
</script>
</head>
<body>
</body>
</html>

  • 错误消息;filename – 发生错误的脚本文件;lineno – 发生错误的行。

脚本文件fibonacci.js代码:

销毁线程
      在线程内部,使用close方法线程自己销毁自己。在线程外部的主线程中,使用线程实例的terminate方法销毁线程。

代码如下:

HTML代码:

//fibonacci.js
var fibonacci = function(n) {
return n < 2 ? n : arguments.callee(n – 1) + arguments.callee(n –
2);
};
onmessage = function(event) {
var n = parseInt(event.data, 10);
postMessage(fibonacci(n));
};

<script type=”text/javascript”>
  onload = function(){
      var worker = new Worker(‘fibonacci.js’);  
      worker.onmessage = function(event) {
        console.log(“Result:” + event.data);
      };
      worker.onerror = function(error) {
        console.log(“Error:” + error.message);
      };
      worker.postMessage(40);
  }  
  </script>
脚本文件fibonacci.js代码:

把它们放到相同的目录,运行页面文件,查看控制台,可以看到运行的结果。
这里还有一点,在主线程中,onmessage事件可以使用另外一种方式挂接:

//fibonacci.js
var fibonacci = function(n) {
    return n < 2 ? n : arguments.callee(n – 1) + arguments.callee(n – 2);
};
onmessage = function(event) {
    var n = parseInt(event.data, 10);
    postMessage(fibonacci(n));
};

代码如下:

把它们放到相同的目录,运行页面文件,查看控制台,可以看到运行的结果。
这里还有一点,在主线程中,onmessage事件可以使用另外一种方式挂接:

相关文章

No Comments, Be The First!
近期评论
    功能
    网站地图xml地图