概述
Web Worker
是 html5
的新特性,JavaScript
是单线程模型,所有任务只能在一个线程上完成,一次只能做一件事。前面的任务没做完,后面的任务只能等着, 在某些场景下很不方便,Web Worker
的作用,就是为 JavaScript
创造多线程环境,允许主线程创建 Worker
线程,将一些任务分配给后者运行。在主线程运行的同时,Worker
线程在后台运行,两者互不干扰。等到 Worker
线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务可以交由 Worker
线程执行,主线程能够保持流畅,不会被阻塞或拖慢。
Worker
线程一旦新建成功,就会始终运行,不会被主线程上的活动(比如用户点击按钮、提交表单)打断。这样有利于随时响应主线程的通信。但是,这也造成了 Worker
比较耗费资源,不应该过度使用,而且一旦使用完毕,就应该关闭
使用场景
任何需要始终运行的js
代码都可以使用Worker
, 比如定时器,当页面处于不可见状态时,定时器的执行间隔会扩大,这样会造成一些程序的执行异常,此时就可以使用webWorkers
来解决
使用方法
-
线程创建
专用线程由Worker()
方法创建,可以接收两个参数,第一个参数是必填的脚本的位置,第二个参数是可选的配置对象,可以指定type
、credentials
、name
三个属性
var worker = new Worker('worker.js')
共享线程使用 Shared Worker() 方法创建,同样支持两个参数,用法与 Worker() 一致。
var sharedWorker = new SharedWorker('shared-worker.js')
注意: 因为 Web Worker 有同源限制,所以在本地调试的时候也需要通过启动本地服务器的方式访问,使用 file:// 协议直接打开的话将会抛出异常,且Worker
线程不能执行alert()
方法和confirm()
方法,但可以使用XMLHttpRequest
对象发出 AJAX 请求
-
Worker 线程和主线程之间的通信
Worker
线程和主线程都通过postMessage()
方法发送消息,通过onmessage
事件接收消息。在这个过程中数据并不是被共享的,postMessage()
一次只能发送一个对象, 如果需要发送多个参数可以将参数包装为数组或对象再进行传递
// 主线程
var worker = new Worker('worker.js')
worker.postMessage([10, 24])
worker.onmessage = function(e) {
console.log(e.data)
}
// Worker 线程
onmessage = function (e) {
if (e.data.length > 1) {
postMessage(e.data[1] - e.data[0])
}
}
在 Worker
线程中,self
和 this
都代表子线程的全局对象。对于监听 message 事件,以下的四种写法是等同的
// 写法 1
self.addEventListener('message', function (e) {
// ...
})
// 写法 2
this.addEventListener('message', function (e) {
// ...
})
// 写法 3
addEventListener('message', function (e) {
// ...
})
// 写法 4
onmessage = function (e) {
// ...
}
- **关闭 Worker **
// 主线程
worker.terminate()
// Dedicated Worker 线程中
self.close()
// Shared Worker 线程中
self.port.close()
在vue中使用Worker
首先要安装laoder
npm i -D worker-loader
在vue.config.js
中配置以下
module.exports = {
// ...
chainWebpack(config) {
config.module
.rule('worker')
.test(/\.worker\.js$/)
.use('worker-loader')
.loader('worker-loader')
.end();
config.module.rule('js').exclude.add(/\.worker\.js$/)
},
parallel: false,
// chainWebpack: config => {
// // 解决:“window is undefined”报错,这个是因为worker线程中不存在window对象,因此不能直接使用,要用this代替
// config.output.globalObject('this')
// }
}
配置了之后 worker.js
后缀的文件就会被 loader
处理
// 在timer.worker.js
let count = 0
// 接收主线程的消息
self.addEventListener('message', (e) => {
count = e.data
self.postMessage(count--)
}, false)
// 用定时器发消息
setInterval(() => {
// postMessage 用于数据交互
self.postMessage(count)
count--
}, 1000)
// 在vue中
import Worker from './prompt.worker.js'
mounted() {
this.worker = new Worker()
this.worker.postMessage('给线程发消息')
this.worker.onmessage = (event) => {
console.log('接收到的消息为:' + event.data)
}
}