大家都知道,JavaScript是单线程的,也就是说,所有的任务只能在一个线程上完成,一次只能做一件事。前面的任务如果没有完成,后面就只能等着。所以,HTML5就提出了web Worker标准,表示JavaScript允许有多个线程,但是子线程完全受主线程的控制,并且子线程不能操作DOM,只有主线程可以操作DOM。所以 Web Worker 的最佳使用场景是执行一些开销较大的数据处理或计算任务。
什么是Web Worker ?
Web Worker 是HTML5标准的一部分,这一规范定义了一套API,它允许一段JavaScript程序运行在主线程之外的另外一个线程中。
值得注意的是, Web Worker 规范中定义了两类工作线程,分别是专用线程Dedicated Worker和共享线程 Shared Worker。其中,Dedicated Worker只能为一个页面所使用,而Shared Worker则可以被多个页面所共享。
如何使用Worker?
使用的时候需要注意的几个地方
同源限制
分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。
DOM限制
Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用document、window、parent这些对象。但是,Worker 线程可以navigator对象和location对象。
通信
Worker 线程和主线程不在同一个上下文环境,所以它们不能直接通信,必须通过发布订阅消息完成。
脚本限制
Worker 线程内不能执行alert()方法和confirm()方法,但是可以使用 XMLHttpRequest 对象发送 AJAX 请求。
文件限制
Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络。
如何创建一个Worker?
Worker构造函数,第一个参数是脚本的网址(必须遵守同源政策),该参数是必需的,且只能加载 JS 脚本,否则报错。
第二个参数是配置对象,该对象可选。它的一个作用就是指定 Worker 的名称,用来区分多个 Worker 线程。
例如创建一个Worker:
const worker = new Worker('worker.js');
主线程与子线程如何通信?
基本原理就是在当前的主线程中加载一个只读文件来创建一个新的线程,两个线程同时存在,且互不阻塞,并且在子线程与主线程之间提供了数据交换的接口postMessage和onmessage。
例如,向Worker子线程发送消息:
// 第一种传递方式
worker.postMessage('我是主线程');
// 第二种传递方式
worker.postMessage({
// ArrayBuffer object
input: buffer
}, [buffer]);
worker.postMessage()方法的参数,就是主线程传给子线程 Worker 的数据。它可以是各种数据类型,包括二进制数据。
接收子线程Work发回的消息
worker.onmessage = function (event) {
console.log('子线程的消息:' + event.data)
}
worker.js子线程向主线程发送消息
self.postMessage('我是子线程')
接收主线程发来的消息
self.onmessage = function (event) {
console.log('主线程的消息:' + event.data)
}
self代表子线程自身,即子线程的全局对象。
以下是主线程与子线程的常用API
主线程中的,worker表示是 Worker 的实例:
worker.postMessage
主线程往worker线程发消息,消息可以是任意类型数据,包括二进制的数据
worker.terminate
主线程关闭worker线程
worker.onmessage
指定worker线程发消息时的回调
也可以通过 worker.addEventListener('message', cb) 的方式
worker.onerror
指定worker线程发生错误时的回调
同样也可以 worker.addEventListener('error', cb)
Worker线程中全局对象为 self,代表子线程自身,这时this指向self:
self.postMessage
worker线程往主线程发消息,消息可以是任意类型数据,包括二进制数据
self.close
worker线程关闭自己
self.onmessage
指定主线程发worker线程消息时的回调
也可以self.addEventListener('message', cb)
self.onerror
指定worker线程发生错误时的回调
也可以 self.addEventListener('error', cb)
self.name
Worker 的名字。该属性只读,由构造函数指定。
载入工具函数
importScripts('work1.js', 'work2.js', ...)
importScripts是同步方法,一旦importScripts方法返回就可以开始使用载入的脚本,而不需要回调函数。
共享线程 SharedWorker
共享线程是为了避免线程的重复创建和销毁过程,降低了系统性能的消耗,共享线程SharedWorker可以同时有多个页面的线程链接。
使用SharedWorker创建共享线程,也需要提供一个javascript脚本文件的URL地址或Blob,该脚本文件中包含了我们在线程中需要执行的代码,如下:
const sharedworker = new SharedWorker("sharedworker.js");
共享线程也使用了message事件监听线程消息,但使用SharedWorker对象的port属性与线程通信如下。
sharedworker.port.onmessage = function (event) {
console.log(event.data)
}
也可以使用SharedWorker对象的port属性向共享线程发送消息
sharedworker.port.postMessage('Hello World');
深圳 · 龙岗 · 大运软件小镇22栋201
电话:400 182 8580
邮箱:szhulian@qq.com