Web Worker:多线程运行JavaScript
Web Worker为Web内容在后台线程中运行脚本提供了一种简单的方法。线程可以执行任务而不干扰用户界面。此外,他们可以使用XMLHttpRequest执行 I/O (尽管responseXML和channel属性总是为空)。一旦创建, 一个worker 可以将消息发送到创建它的JavaScript代码, 通过将消息发布到该代码指定的事件处理程序(反之亦然)。本文提供了有关使用Web Worker的详细介绍。
众所周知,JavaScript 是单线程运行的。也就是说,所有任务只能在一个线程上完成,前面的任务没做完,后面的任务只能等着。
那怎么才能避免单线程遇到的一些问题呢?传统的方法是:我们会通过 setTimeout()、setInterval()、ajax 和事件处理程序等技术模拟异步,但只是解决了一部分 JavaScript 的单线程限制。那有没有更好的方法呢?答案是有的,请往下看...
如何多线程运行JavaScript?
Web Workers 是浏览器内置的线程所以可以被用来执行非阻塞事件循环的 JavaScript 代码; 是 HTML5 标准的一部分,这一规范定义了一套 API,为Web内容在后台线程中运行脚本提供了一种简单的方法;它允许一段 JavaScript 程序运行在主线程之外的另外一个线程中,可以执行任务而不干扰用户界面。
正因为如此,在浏览器环境下处理 CPU 计算密集型任务的而不会阻塞、卡顿已成为可能。一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)不会被阻塞或拖慢,从而让程序运行流畅。
值得注意的是,规范中有三种类型的 Web Workers:
- Dedicated Workers
- Shared Workers
- Service workers
本文会依次介绍这三种类型的使用方式!
Dedicated Workers
Dedicated Workers仅仅能被生成它的脚本所使用,下面就创建一个新的worker很简单。需要做的是调用Worker() 的构造器,指定一个脚本的URI来执行worker线程。
var myWorker = new Worker('worker.js');
消息的接收和发送
workers通过postMessage() 方法和onmessage事件处理函数进行消息的接受和发送。向一个worker发送消息需要这样做:
myWorker.postMessage('主线程发送消息到worker:Hello World!');
在worker中接收到消息后,可以写这样一个事件处理函数代码作为响应:
onmessage = function(e) {
console.log('Message received from main script');
var workerResult = '收到消息: ' + e.data;
}
onmessage处理函数允许我们在任何时刻,一旦接收到消息就可以执行一些代码,代码中消息本身作为事件的data属性进行使用。还可以使用postMessage()方法,将结果回传给主线程。
postMessage(workerResult);
回到主线程,我们再次使用onmessage以响应worker回传的消息
myWorker.onmessage = function(e) {
console.log('收到worker发送到消息' + e.data);
}
注意: 当一个消息在主线程和worker之间传递时,它被复制或者转移了,而不是共享 在主线程中使用时,onmessage和postMessage() 必须挂在worker对象上,而在worker中使用时不用这样做。原因是,在worker内部,worker是有效的全局作用域。
终止worker
如果你需要从主线程中立刻终止一个运行中的worker,可以调用worker的terminate 方法
myWorker.terminate();
worker 线程会被立即杀死,不会有任何机会让它完成自己的操作或清理工作。
而在worker线程中,workers 也可以调用自己的 close 方法进行关闭:
close();
worker引入脚本与库
Worker 线程能够访问一个全局函数importScripts()来引入脚本,该函数接受0个或者多个URI作为参数来引入资源;
importScripts('script1.js', 'script2.js');
错误处理
当 worker 出现运行中错误时,它的 onerror 事件处理函数会被调用。它会收到一个扩展了 ErrorEvent 接口的名为 error的事件。 该事件不会冒泡并且可以被取消;为了防止触发默认动作,worker 可以调用错误事件的 preventDefault()方法。
worker.onerror(function (e) {
console.log(e);
});
Worker 内部也可以监听error事件。
场景案例
待补充
Shared Workers
一个共享worker可以被多个脚本使用——即使这些脚本正在被不同的window、iframe或者worker访问。生成一个新的共享worker与生成一个专用worker非常相似,只是构造器的名字不同,例如:
var myWorker = new SharedWorker('worker.js');
一个非常大的区别在于,与一个共享worker通信必须通过端口对象——一个确切的打开的端口供脚本与worker通信(在专用worker中这一部分是隐式进行的)
在传递消息之前,端口连接必须被显式的打开,打开方式是使用onmessage事件处理函数或者start()方法。
myWorker.port.start(); // 父级线程中的调用
port.start(); // worker线程中的调用, 假设port变量代表一个端口
消息的接收和发送
消息可以像之前那样发送到worker了,但是postMessage() 方法必须被端口对象调用
myWorker.port.postMessage('hello world');
回到worker中,这里也有一些些复杂
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,我们检查结果的类型,然后将运算结果放入结果段落中合适的地方。
场景案例
待补充
- 本文类型: 原创
- 本文出处:
- 版权说明: 本站内容均采用©BY-NC-SA许可协议,版权归作者和本站所有!欢迎转载,但未经作者同意必须在文章页面注明原文出处,否则保留追究法律责任的权利。