12.28 HTML 网页工作线程(Web Worker)
Web worker是一个在后台运行的JavaScript程序,它不会影响当前页面的用户操作。 从事过Windows桌面编程的人,应该熟悉工作线程(Work Thread)的概念,该线程主要负责比较耗费时间的数据处理或通信方面的任务,当任务处理完成时,发送消息通知界面主线程(UI Thread)。 这样的多线程机制可以大大提高应用程序处理数据的能力,我们在前言中说过HTML5的设计目的之一就是想尽可能结合传统本地应用和网页之间的优点,Web Worker就是这样一个例子。
Web Worker的好处
和C++(版本>=11)以及Go这些系统级语言内置支持多线程并行计算不同,网页JavaScript脚本只支持单线程,如果在HTML页面中执行脚本,那么该页面将不能响应用户交互,直到脚本执行完成。 那么如果不幸该脚本在等待某个网络数据包或者正在执行一个大的循环处理,页面就将处于僵死状态。
Web Workers 是HTML5提供的一个JavaScript多线程解决方案。通过使用Web worker,执行任务交付给浏览器在后台默默运行,而不会影响当前页面的性能,用户可以继续任何其他操作,这将大大提升用户体验。
浏览器支持
下面表格中的数字表示最早支持的版本号。
API | |||||
---|---|---|---|---|---|
Web Workers | 4.0 | 10.0 | 3.5 | 4.0 | 11.5 |
使用示例
下面的例子演示了一个计数的网页工作线程:
计数:var w; function startWorker1() { if(typeof(Worker)!=="undefined") { if(typeof(w)=="undefined") { w=new Worker("/themes/guide/js/demo_workers.js"); } w.onmessage = function (event) { document.getElementById("result1").innerHTML=event.data; }; } else { document.getElementById("result1").innerHTML="Sorry, your browser does not support Web Workers..."; } } function stopWorker1() { w.terminate(); w = undefined; }
下面我们看看这个例子是如何工作的。
检查浏览器支持
并非所有浏览器都支持该API,因此在创建Web worker之前,我们需要先检测该特性是否被用户浏览器所支持:
if(typeof(Worker) !== "undefined") { // Yes! Web worker support! // Some code..... } else { // Sorry! No Web Worker support.. }
创建一个Web Worker文件
我们新建一个外部JavaScript文件,文件名为"demo_workers.js",代码如下(简单的计数):
var i = 0; function timedCount() { i = i + 1; postMessage(i); setTimeout("timedCount()",500); } timedCount();
上述代码中的postMessage方法很重要,依赖于该方法,工作线程才能和HTML页面(用户界面)之间通信,该方法发送一个消息回给页面。
创建一个Web Worker对象
我们已经有了上述的web worker文件,现在我们需要从HTML页面调用它。
下面这行代码检查worker是否已存在,如果不存在,则创建一个新的Web Worker对象,并运行"demo_workers.js"文件中的代码:
if(typeof(w) == "undefined") { w = new Worker("demo_workers.js"); }
然后我们可以在Web worker中发送和接收消息。我们给该Web worker添加一个"onmessage"事件侦听器。
w.onmessage = function(event){ document.getElementById("result").innerHTML = event.data; };
当web worker发回一个消息时,页面事件侦听器中的代码被执行。web worker发回的数据被保存在event.data中。
终止Web Worker
当一个Web worker对象被创建后,它将持续侦听消息(即使外部脚本已经执行完成),除非被终止。
要终止一个web worker,释放相关的浏览器/计算机资源,使用 terminate() 方法:
w.terminate();
重用Web Worker
如果您在worker被终止后,将worker变量设置为未定义(undefined
),就可以被重用:
w = undefined;
完整的示例代码
Web worker的代码在外部.js文件中,HTML页面的代码如下:
Count numbers:
var w; function startWorker() { if(typeof(Worker) !== "undefined") { if(typeof(w) == "undefined") { w = new Worker("demo_workers.js"); } w.onmessage = function(event) { document.getElementById("result").innerHTML = event.data; }; } else { document.getElementById("result").innerHTML = "Sorry! No Web Worker support."; } } function stopWorker() { w.terminate(); w = undefined; }
Web Worker的限制
Web workers不能访问如下的JavaScript对象,即不能操作页面DOM树:
- window 对象
- document 对象
- parent 对象
Web worker不能跨源(域名/协议/端口的组合)访问。比如我们不能把demo-worker.js文件放在 xnip.cn 主域名下,却要在 www.xnip.cn 中调用执行。
此外,熟悉多线程的同学,需要记住多线程不是免费的午餐,线程的频繁创建和销毁都是需要花费资源和时间的,有测试数据显示Web worker的线程创建需要消耗40ms的时间,而发送消息需要耗费1ms左右的时间。
所以某些场合下,使用ajax异步回调机制是比Web worker这种多线程+消息通知机制更快更简洁的选择方案。
更为详细的性能测试,请参考阅读:https://hacks.mozilla.org/2015/07/how-fast-are-web-workers/