Web Worker
作用
Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。
优点
- 一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢
- Worker 线程一旦新建成功,就会始终运行,不会被主线程上的活动(比如用户点击按钮,提交表单)打断。这样有利于随时响应主线程的通信
缺点
- Worder比较耗费资源,不应该过度使用,而且一旦使用完毕,就应该关闭
使用限制
-
同源限制
分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源;(即:两种文件在同一个目录文件中)
-
DOM限制
Worder 线程无法操作 DOM结构 ,也就是无法使用
document
、window
、parent
这些对象。 Worker 线程可以操作部分BOM,可以使用
navigator
对象和location
对象 -
通信联系限制
Worker 线程和主线程不再同一个上下文环境,它们不能直接通信,必须通过消息完成。
-
脚本限制
Worker 线程不能执行
alert()
方法和confirm()
方法,但可以使用XMLHttpRequest
对象发出的AJAX
请求 -
文件限制
Worker 线程无法读取本地文件,即不能打开本机的文件系统(
file://
),它所加载的脚本,必须来自网络
主线程
创建一个 worker 线程
let worker1 = new Worker("worker1.js");
向 worker 线程传递数据
-
如果需要传递参数,可以使用 worker.postMessage();
-
参数可以为任意数据类型
worker1.postMessage("参数");
message 事件(接收数据)
-
在主线程中,通过 onMessage 事件监听,接收 worker1传回来的数据
-
回来的数据保存在 event.data 中
worker1.onMessage = function(event) {
console.log(event.data);
}
//也可以通过 addEventListenr 来监听该事件
error 事件
- 在主线程中可以通过 error 事件监听 worker 线程是否发生错误
- 如果发生错误,会触发该事件
worker1.onerror = function(event) {
console.log(event);
console.log("worker线程出错");
}
//也可以通过 addEventListenr 来监听该事件
关闭 worker 线程
- 在主线程中关闭 worker1 线程
worker1.terminate();
拓展: Worker( )
Worker( ) 构造函数,可以接收两个参数。
- 第一个参数是脚本的网址(必须遵守同源政策),该参数是必须的,且只能加载 JS 脚本,否则会报错。
- 第二个参数是配置对象,该对象可选。它的一个作用就是指定 Worker 的名称,用来区分多个 Worker线程
// 主线程
let myWorker = new Worker("worker.js",{name:"myWorker"});
// worker 线程
self.name // myWorker
Worker( ) 构造函数返回一个 Worker 线程对象,用来供主线程操作 Worker。 Worker 线程对象的属性和方法如下:
Worker.onerror:指定 error 事件的监听函数。
Worker.onmessage:指定 message 事件的监听函数,发送过来的数据在Event.data属性中。
Worker.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。
Worker.postMessage():向 Worker 线程发送消息。
Worker.terminate():立即终止 Worker 线程。
Worker 线程
★ self
在 Worker 线程中没有 window 对象, 用 self 代替,表示全局,在 worker 线程中写的时候可以省略self
message 事件(接收数据)
-
通过 message 事件,用来获取从主线程传递过来的数据
-
接收的数据也保存在 event.data 中
//方法一:
self.addEventListener("message",function(event) {
console.log(event.data);
})
//方法二:
self.onmessage = function(event) {
console.log(event.data);
}
error事件
- 在worker线程中也可以通过 error 事件监听自身是否发生错误
- 如果发生错误,会触发该事件
- 注意!!!!!! error 事件监听应该写在出现错误的前面才能触发
self.onerror = function(event) {
console.log(event);
console.log("worker线程出错");
}
//也可以通过 addEventListenr 来监听该事件
向主线程传递数据
-
通过 self.postMessage() 将数据传递给主线程
-
参数可以为任意数据类型
self.postMessage("参数");
加载其他脚本文件
- 通过 importScript( ) 方法可以加载其他脚本,该方法也可以同时加载多个脚本
// 用法1
importScripts("script1.js");
// 用法2
importScripts("script1.js","script2.js");
self.importScripts("script1.js");
关闭自身
- 在 worker 线程内部关闭自身,在worker线程中 self 可写可不写
close();
self.close();
Worker 线程自己的全局属性和方法
self.name: Worker 的名字。该属性只读,由构造函数指定。
self.onmessage:指定message事件的监听函数。
self.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。
self.close():关闭 Worker 线程。
self.postMessage():向产生这个 Worker 线程发送消息。
self.importScripts():加载 JS 脚本。
数据通信
-
主线程与 Worker 线程之间的通信内容,可以是文本,也可以是对象。
-
这种通信是拷贝关系,即是传值而不是传地址, Worker 对通信内容的修改,不会影响到主线程。
-
浏览器内容的运行机制:
先将通信内容串行化,然后把串行化后的字符串发给 Worker ,后者再将它还原。
-
主线程与 Worker 之间也可以交换二进制数据
-
拓展:
- 串行化也叫做序列化,就是把存在于内存的对象数据转化成可以保存成硬盘文件的形式去存储
- 序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题
-
但是,拷贝方式发送的二进制数据,会造成性能问题
- 比如,主线程向 Worker 线程发送一个 500 MB 的文件,默认情况下,浏览器会生成一个原文件的拷贝
- 为了解决这个问题, JavaScript 运行主线程把二进制数据直接转移给子线程,但是一旦转移,主线程就无法再使用这些二进制数据了。(这是为了防止出现多个线程同时修改数据的麻烦局面)
- 这种转移数据的方法,叫做 Transferable Objects,这使得主线程可以快速把数据交给 Worker ,对于影像处理,声音处理,3D运算等就非常方便了,不会产生性能负担
-
转移数据的控制权
// Transferable Objects 格式 worker.postMessage(arrayBuffer, [arrayBuffer]); // 例子 var ab = new ArrayBuffer(1); worker.postMessage(ab, [ab]); // ArrayBuffer 为类型化数组,数据保存在栈中