【HTML5】HTML5 Web Worker

  基础介绍:Introduction to the HTML5 Web Workers: the JavaScript multithreading approach

  首先本文只讨论 window.Worker  而不包含SharedWorker .毕竟这玩意连firefox4 beta7 都还没有支持.(貌似chrome safari opera 的较新版本都已经支持.)

  有一点要提前说明的是:worker线程中的代码具有独立的执行环境. 我们可以把它假想成一个mini的global环境. 有兴趣的同学可以去看相关文档. 在这个执行环境中.不要在奢望你能调用绝大多数宿主提供给我们的对象、属性、方法.你大概可以认为,我们只能用的上js内置对象和方法.其他的一律不考虑. 不过,在该执行上下文内.我们可以使用这样一些东东:

  self : 即当前worker 的GlobalWorker对象. 这上面有些特殊的宿主对象.比如 location、navigator、setTimeout、setInterval之类的.  一个好消息是我们可以调用 JSON 对象. 剩下的基本就是我们下面要介绍的一些方法喽.

  worker有同源策略限制. 即worker无法引入一个跨域的资源来直接使用.(但是worker的一个方法可以绕过同源策略限制,来动态加载其他域脚本)

  浏览器支持:  

  • IE: 无 (IE10已开始支持.)

  • FireFox: 3.5+

  • Safair: 4+                         

  • Chrome 3+

  • Opera : 10+

   主要接口: (详细API可自行查看)

  接口定义相关链接 : http://www.whatwg.org/specs/web-workers/current-work/#the-workerglobalscope-abstract-interface

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

   outside :  

   事件:

   1.onmessage  事件 :当a.js 即 worker线程中调用 postMessage(sData)方法时, onmessage可以侦听该事件,并执行指定回调方法

worker.onmessage = function(e){

     e.data// 我们需要的sData 

}

  2.onerror   事件 : 捕获a.js中 worker线程中语法 异常和运行时各类异常.(注:这玩意也管 404那种加载失败滴异常.除了某些浏览器...)

worker.onerror = function(e){

     e.message // 错误信息

     e.lineno // 行号

     e.filename //返回完整的错误文件的url .

};

注:worker.addEventListener('message',function(e){...});// 这样也中.

 

   方法:

     1. postMessage 方法 : 向 worker 发送消息.  worker线程 必须同时注册onmessage事件.

     2. terminate 方法 : 停掉worker的行为.

 

  inside :  

   事件:

   1.onmessage  事件 :当out页调用 worker.postMessage(sData)方法时, onmessage可以侦听该事件,并执行指定回调方法

方法:

  2 . postMessage 方法 : 向 out页发送消息.  out页必须同时注册worker.onmessage事件.

  3 . close 方法 : 停掉worker的行为.

  4 . importScripts : 方法,worker线程动态加载外部脚本用. 该方法会冻结worker线程.直到动态加载脚本加载完毕或执行完毕(浏览器差异)

   5.importScripts 方法支持同事加载多个脚本:importScripts('a.js','b.js','c.js');

   6.同事加载多个脚本在各个浏览器中均为并行加载.(前提是HTTP连接数够用).

   7.但执行顺序是严格按照参数顺序进行的.即 先执行a 后执行b 然后才是c. 无论哪个先加载完.(FF有差异) 

 

  遗憾的是 safari4 在最初支持了 worker的同时,并没有提供原生 JSON对象的支持. 如果我们的项目不考虑支持safari4,那么真是个好消息. 否则,可能为了让worker更健壮的工作,我们需要自己写一个JSON转换了. 

更加遗憾的是.对于web worker 的一些 行为,浏览器实现,也有比较大的差异.

 

   浏览器差异:

 

   关于异常:

   opera中, 一旦发生一个语法错误或运行时错误.有时候会多抛出一个 Internal error 给你. 并且lineno 总是0.

opera中, 一般的语法错误或执行期异常(如调用未定义变量,throw new Error(s)除外) 总是无法给出准确的错误信息.且lineno 总是0.

   chrome 和 safari 中, worker.onerror 并不捕获加载失败的404异常.chrome 作为 worker 的先驱.值得鄙视一下.

 

  关于worker引入文件的缓存问题:

   opera中,一但.js被缓存那么,即使右键-重新载入 ,也会直接去cache读. 你不用指望会有什么304 或200.这个真没有.

我们有2个选择,要么完全放弃客户端缓存, 要么就更换版本号...好纠结的问题.

 

  调用close()方法后的差异

   opera中,一但在worker线程中调用close()方法.那么就代表着一切都结束了.如果在close(); 后你仍然试图使用worker中的某些属性、事件、或方法则会抛出一个Internal error . 但其他浏览器则不会.

opera中,调用close()方法后, 出现的postMessage('')给主页面传递信息,不会再触发主页面的onmessage回调. 其他浏览器则可以.

 

   FF 中, 调用close()后 worker 虽然不再响应页面的postMessage .但是在close();调用之前.页面postMessage给worker线程的信息.则仍然会在.worker线程结束后被触发回调. 其他浏览器则不会.

chrome中 ,调用close()后. self对象被赋值为null . 但你仍然可以和其他浏览器一样 直接调用 WorkerGlobalScope对象的方法.所以,应避免使用self.postMessage, self.onmessage=function(){} ,self.close();而采用直接调用的方式.(如onmessage=function(){};)

 

   关于worker.terminate()方法后的差异

   opera中,主页面调用此方法后. 再调用worker.postMessage(sData)会抛出异常. 其他浏览器则不会.(虽然此时,worker.postMessage仍然是一个方法)

 

   opera 中worker.terminate() 带来的问题:

   1. worker.onerror 触发回调时要在主页面并不是在使用worker的脚本退出所在执行环境后才会被触发.而其他浏览器则如此. 考虑下面的代码:

var worker = new Worker("error.js");//抛出一个异常.

worker.onerror = function(e){

     alert(e);//opera则会触发. 其他浏览器则不会.

};

var t = new Date;

while(new Date - t < 3000);

worker.terminate();//问题就出在 这里...

   2. worker.onmessage 也存在同样的问题. 考虑下面的代码 :

var worker = new Worker("b.js"); //b.js中 第一时间postMessage(sData);

worker.onmessage=function(e){

     alert(e.data);//只有opera 会触发b.js中的这次回调. 并打印出e.data

}

var t = new Date;

while(new Date - t < 3000);

worker.terminate();

 

  关于importScripts方法:

   FF中,importScripts方法加载脚本时. 虽然同样会冻结当前worker线程.但执行期是在worker线程结束后..即,其他浏览器则不是如此..其他浏览器的 加载 执行,都会冻结住worker的线程.

 

   FF中,importScripts 方法加载多个脚本时,执行顺序不确定.应是先到先执行.

 

   FF safari chrome 中,importScripts方法加载脚本失败时,会抛出异常,且可被out页面的 worker.onerror 捕获,opera浏览器则不会.

 

   FF中,importScripts方法加载多个脚本如a.js,b.js,c.js,虽然是并行加载.但执行却要等待a b c三个文件都加载完毕后才开始执行. 期间 任何一个文件404错误.都将导致直接退出当前执行环境并且a.js b.js c.js 以及worker自身代码都将得不到执行. 其他浏览器则不是这样.

 

   safari chrome,使用importScripts同时加载多个脚本时,任何一个404错误,都将导致退出当前执行环境.  但先加载好的外部脚本可以被正常执行.

opera 则无视404错误.当它不存在.继续干后面的活.

 

  关于worker内创建worker的差异:

  safari chrome中,不允许worker内部再次创建一个worker. FF 和 opera 则可以. (即safari chrome WorkerGlobal不具备一个名为Worker的成员)

  总结: 

  • 对于worker对象没有onload事件.我们不必疑惑.因为你在worker加载的.js没有加载好之前的,所有postMessage 都会在worker 加载并且执行完毕后,被触发回调.所以我们完全不必担心这个问题.

  • 对于跨浏览器的worker.我们遵守 逻辑不依赖执行顺序,可以很好的避免一些,看起来很诧异的问题.

  • worker 内部可以继续创建另外一个worker.用于多线程并行运算于交互.对于多个worker及页面间的共享通讯.请参考SharedWorker 对象.目前浏览器支持还有待完善. 但是 worer内部的worker仍然不可以跨域.即worker引用资源的url 必须是相对路径(即使引入当前worker的脚本是一个位于其他域的.被importScripts引入到外层worker中的脚本.相对路径也永远是相对 outside 页面的路径).或同域的绝对路径. 

  • 对于IE 不支持这东西. 暂时没什么好办法,无非是用 setTimeout 之类的去模拟多线程.  毕竟我们用worker 的最终目的,在不影响UI update 及 浏览器与用户交互的情况下, 可以做大规模运算.

你可能感兴趣的:(【HTML5】HTML5 Web Worker)