JavaScript 多线程Web Worker && 在Vue中如何使用

概述

Web Workerhtml5 的新特性,JavaScript 是单线程模型,所有任务只能在一个线程上完成,一次只能做一件事。前面的任务没做完,后面的任务只能等着, 在某些场景下很不方便,Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务可以交由 Worker 线程执行,主线程能够保持流畅,不会被阻塞或拖慢。
Worker 线程一旦新建成功,就会始终运行,不会被主线程上的活动(比如用户点击按钮、提交表单)打断。这样有利于随时响应主线程的通信。但是,这也造成了 Worker比较耗费资源,不应该过度使用,而且一旦使用完毕,就应该关闭

使用场景

任何需要始终运行的js代码都可以使用Worker , 比如定时器,当页面处于不可见状态时,定时器的执行间隔会扩大,这样会造成一些程序的执行异常,此时就可以使用webWorkers来解决

使用方法

  1. 线程创建
    专用线程由 Worker() 方法创建,可以接收两个参数,第一个参数是必填的脚本的位置,第二个参数是可选的配置对象,可以指定 typecredentialsname三个属性
var worker = new Worker('worker.js')

共享线程使用 Shared Worker() 方法创建,同样支持两个参数,用法与 Worker() 一致。

var sharedWorker = new SharedWorker('shared-worker.js')

注意: 因为 Web Worker 有同源限制,所以在本地调试的时候也需要通过启动本地服务器的方式访问,使用 file:// 协议直接打开的话将会抛出异常,且Worker线程不能执行alert()方法和confirm()方法,但可以使用XMLHttpRequest对象发出 AJAX 请求

  1. 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 线程中,selfthis 都代表子线程的全局对象。对于监听 message 事件,以下的四种写法是等同的

// 写法 1
self.addEventListener('message', function (e) {
    // ...
})

// 写法 2
this.addEventListener('message', function (e) {
    // ...
})

// 写法 3
addEventListener('message', function (e) {
    // ...
})

// 写法 4
onmessage = function (e) {
    // ...
}
  1. **关闭 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)
  } 
}

worker 的更多API可以查看Worker MDN

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容