Web Workers文档

Web Worker为Web内容在后台线程中运行脚本提供了一种简单的方法。线程可以执行任务而不干扰用户界面。此外,他们可以使用XMLHttpRequest执行 I/O  (尽管responseXML和通道属性总是为空)。一旦创建, 一个worker 可以将消息发送到创建它的JavaScript代码, 通过将消息发布到该代码指定的事件处理程序 (反之亦然)。本文提供了有关使用Web Worker的详细介绍。

Web Workers API

一个worker是使用一个构造函数创建的一个对象(e.g. Worker()) 运行一个命名的JavaScript文件 - 这个文件包含将在工作线程中运行的代码; workers 运行在另一个全局上下文中,不同于当前的window. 因此,使用 window快捷方式获取当前全局的范围 (而不是self) 在一个 Worker内将返回错误。

在专用workers的情况下,DedicatedWorkerGlobalScope 对象代表了worker的上下文(专用workers是指标准worker仅在单一脚本中被使用;共享worker的上下文是SharedWorkerGlobalScope对象)。一个专用worker仅仅能被首次生成它的脚本使用,而共享worker可以同时被多个脚本使用。
注意:参照 The Web Workers API landing page 获取workers的参考文档和更多指引。

在worker线程中你可以运行任何你喜欢的代码,不过有一些例外情况。比如:在worker内直接操作DOM节点,或者使用window对象的默认方法和属性。然而你可以使用大量window对象之下的东西,包括WebSockets,IndexedDB以及FireFox OS专用的Data Store API等数据存储机制。查看Functions and classes available to workers获取详情。

workers和主线程间的数据传递通过这样的消息机制进行——双方都使用postMessage()方法发送各自的消息,使用onmessage事件处理函数来响应消息(消息被包含在Message事件的data属性中)。这个过程中数据并不是被共享而是被复制。

只要运行在同源的父页面中,workers可以依次生成新的workers;并且可以使用XMLHttpRequest 进行网络I/O,responseXML和XMLHttpRequest的通道属性一直返回null的情况除外。

专用worker

如前文所述,一个专用worker仅仅能被生成它的脚本所使用。这一部分将探讨 专用worker基础示例 (运行专用worker) 中的JavaScript代码:将你输入的2个数字作乘法。输入的数字会发送给一个专用worker,由专用worker作乘法后,再返回给页面进行展示。

这个例子很小,但是我们决定在保持简单的同时向你介绍基础的worker概念。更多的细节会在之后的文章中进行讲解。

worker特性检测

为了更好的错误处理控制以及向下兼容,将你的worker运行代码包裹在以下代码中是一个很好的想法(main.js):

if (window.Worker) { ... }

生成一个专用worker

创建一个新的worker很简单。你需要做的是调用Worker() 的构造器,指定一个脚本的URI来执行worker线程(main.js):

var myWorker = new Worker('worker.js');

专用worker中消息的接收和发送

workers的魔法通过postMessage() 方法和onmessage事件处理函数生效。向一个worker发送消息需要这样做(main.js):

first.onchange = function() { myWorker.postMessage([first.value,second.value]); console.log('Message posted to worker'); } second.onchange = function() { myWorker.postMessage([first.value,second.value]); console.log('Message posted to worker'); }

这段代码中变量first和second代表2个<input>元素;它们当中任意一个的值发生改变时,myWorker.postMessage([first.value,second.value])会将这2个值组成数组发送给worker。你可以在消息中发送许多你想发送的东西。

在worker中接收到消息后,我们可以写这样一个事件处理函数代码作为响应(worker.js):

onmessage = function(e) { console.log('Message received from main script'); var workerResult = 'Result: ' + (e.data[0] * e.data[1]); console.log('Posting message back to main script'); postMessage(workerResult); }

onmessage处理函数允许我们在任何时刻,一旦接收到消息就可以执行一些代码,代码中消息本身作为事件的data属性进行使用。这里我们简单的对这2个数字作乘法处理并再次使用postMessage()方法,将结果回传给主线程。

回到主线程,我们再次使用onmessage以响应worker回传的消息:

myWorker.onmessage = function(e) { result.textContent = e.data; console.log('Message received from worker'); }

在这里我们获取消息事件的data,并且将它设置为result的textContent,所以用户可以直接看到运算的结果。

注意: 作为参数传递给worker构造器的URI必须遵循同源策略 。

目前各浏览器供应商对于何种URI属于同源仍有争议;Gecko 10.0 (Firefox 10.0 / Thunderbird 10.0 / SeaMonkey 2.7)及更高版本支持数据URI 而 Internet Explorer 10 不支持将blob URI作为worker中合法的脚本。

注意: 在主线程中使用时,onmessagepostMessage() 必须挂在worker对象上,而在worker中使用时不用这样做。原因是,在worker内部,worker是有效的全局作用域。

注意: 当一个消息在主线程和worker之间传递时,它被复制或者转移了,而不是共享。请参阅Transferring data to and from workers: further details 获取更详尽的解释。

终止worker

如果你需要从主线程中立刻终止一个运行中的worker,可以调用worker的terminate 方法:

myWorker.terminate();

worker 线程会被立即杀死,不会有任何机会让它完成自己的操作或清理工作。

而在worker线程中,workers 也可以调用自己的 close  方法进行关闭:

close();

处理错误

当 worker 出现运行中错误时,它的 onerror 事件处理函数会被调用。它会收到一个扩展了 ErrorEvent 接口的名为 error的事件。

该事件不会冒泡并且可以被取消;为了防止触发默认动作,worker 可以调用错误事件的 preventDefault() 方法。

错误事件有以下三个用户关心的字段:

message可读性良好的错误消息。filename发生错误的脚本文件名。lineno发生错误时所在脚本文件的行号。

生成subworker

如果需要的话 worker 能够生成更多的 worker。这就是所谓的subworker,它们必须托管在同源的父页面内。而且,subworker 解析 URI 时会相对于父 worker 的地址而不是自身页面的地址。这使得 worker 更容易记录它们之间的依赖关系。

引入脚本与库

Worker 线程能够访问一个全局函数importScripts()来引入脚本,该函数接受0个或者多个URI作为参数来引入资源;以下例子都是合法的:

importScripts();                        /* 什么都不引入 */ importScripts('foo.js'); /* 只引入 "foo.js" */ importScripts('foo.js', 'bar.js'); /* 引入两个脚本 */

浏览器加载并运行每一个列出的脚本。每个脚本中的全局对象都能够被 worker 使用。如果脚本无法加载,将抛出 NETWORK_ERROR 异常,接下来的代码也无法执行。而之前执行的代码(包括使用 window.setTimeout() 异步执行的代码)依然能够运行。importScripts() 之后的函数声明依然会被保留,因为它们始终会在其他代码之前运行。
注意: 脚本的下载顺序不固定,但执行时会按照传入 importScripts() 中的文件名顺序进行。这个过程是同步完成的;直到所有脚本都下载并运行完毕, importScripts() 才会返回。

共享worker

一个共享worker可以被多个脚本使用——即使这些脚本正在被不同的window、iframe或者worker访问。这一部分,我们会讨论共享worker基础示例运行共享worker)中的javascript代码:该示例与专用worker基础示例非常相像,只是有2个可用函数被存放在不同脚本文件中:两数相乘函数,以及求平方函数。这两个脚本用同一个worker来完成实际需要的运算。

这里,我们关注一下专用worker和共享worker之间的区别。在这个示例中有2个HTML页面,每个页面所包含的javascript代码使用的是同一个worker。

注意:如果共享worker可以被多个浏览上下文调用,所有这些浏览上下文必须属于同源(相同的协议,主机和端口号)。 

注意:在 Firefox中, 共享worker不能被私有和非私有window对象的document所共享 (bug 1177621)。

生成一个共享worker

生成一个新的共享worker与生成一个专用worker非常相似,只是构造器的名字不同(查看 index.html 和 index2.html)——生成共享worker的代码如下:

var myWorker = new SharedWorker('worker.js');

一个非常大的区别在于,与一个共享worker通信必须通过端口对象——一个确切的打开的端口供脚本与worker通信(在专用worker中这一部分是隐式进行的)。

在传递消息之前,端口连接必须被显式的打开,打开方式是使用onmessage事件处理函数或者start()方法。尽管示例中的 multiply.js 和 worker.js 文件调用了start()方法,这些调用并不那么重要因为onmessage事件处理函数正在被使用。start()方法的调用只在一种情况下需要,那就是消息事件被addEventListener()方法使用。

在使用start()方法打开端口连接时,如果父级线程和worker线程需要双向通信,那么它们都需要调用start()方法。

myWorker.port.start(); // 父级线程中的调用
port.start(); // worker线程中的调用, 假设port变量代表一个端口

共享worker中消息的接收和发送

现在,消息可以像之前那样发送到worker了,但是postMessage() 方法必须被端口对象调用(你会再一次看到 multiply.js 和 square.js中相似的结构):

squareNumber.onchange = function() { myWorker.port.postMessage([squareNumber.value,squareNumber.value]); console.log('Message posted to worker'); }

回到worker中,这里也有一些些复杂(worker.js):

onconnect = function(e) { var port = e.ports[0]; port.onmessage = function(e) { var workerResult = 'Result: ' + (e.data[0] * e.data[1]); port.postMessage(workerResult); } }

首先,当一个端口连接被创建时(例如:在父级线程中,设置onmessage事件处理函数,或者显式调用start()方法时),使用onconnect事件处理函数来执行代码。

使用事件的ports属性来获取端口并存储在变量中。

然后,为端口添加一个消息处理函数用来做运算并回传结果给主线程。在worker线程中设置此消息处理函数也会隐式的打开与主线程的端口连接,因此这里跟前文一样,对port.start()的调用也是不必要的。

最后,回到主脚本,我们处理消息(你会又一次看到 multiply.js 和 square.js中相似的结构):

myWorker.port.onmessage = function(e) { result2.textContent = e.data; console.log('Message received from worker'); }

当一条消息通过端口回到worker,我们检查结果的类型,然后将运算结果放入结果段落中合适的地方。

关于线程安全

Worker接口会生成真正的操作系统级别的线程,如果你不太小心,那么并发(concurrency)会对你的代码产生有趣的影响。然而,对于 web worker 来说,与其他线程的通信点会被很小心的控制,这意味着你很难引起并发问题。你没有办法去访问非线程安全的组件或者是 DOM,此外你还需要通过序列化对象来与线程交互特定的数据。所以你要是不费点劲儿,还真搞不出错误来。

 

HTML内容

<html>
<head> <title>Multithreading Catastrophy</title> <style> body { margin: 0px; } canvas { position: absolute; top: 0; bottom: 0; left: 0; right:0; width: 100%; height: 100%; } </style> <script src="main.js" async></script> </head> <body> <canvas id="canvas"></canvas> </body> </html>

 

main.js内容

// main.js
var myworker = new Worker("worker.js"), width=window.innerWidth, height=window.innerHeight, context=document.getElementById('canvas').getContext('2d'); var imagedatatmp=context.createImageData(width,height); myworker.onmessage = function(data){ imageData = imagedatatmp.from(data); }; setTimeout(function draw_canvas() { context.putImageData(imageData); setTimeout(draw_canvas, 1000/60); },10); window.onresize = window.reload; // Quick (to type) n' dirty way to resize;

 

worker.js内容

// worker.js
window.onmessage = function(width, height){ var noise = function(x, y, z) { var p = new Array(512), permutation = [151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208

转载于:https://www.cnblogs.com/cangqinglang/p/8989153.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值