关于WebWorker的基础概念

本文详细介绍了WebWorker在JavaScript中的作用,如何创建和使用Worker线程处理计算密集型任务,以及主线程与Worker线程之间的数据交换、错误处理和关闭机制。同时,文章还讨论了SharedWorker的特点及其在跨窗口和iframe环境的应用。
摘要由CSDN通过智能技术生成

1 前言

js 最初是设计到浏览器中运行的,为了防止多个线程操作 DOM,js 执行器被设计为单线程,但是碰到大量计算的场景时( 图像处理,视频解码等),js 线程会造成长时间阻塞,影响用户体验,为解决这一弊端,Web Worker 应运而生

2 关于 Web Wroker

Web Worker是 HTML5 标准的一部分,这一规范定义了一系列 API,允许我们在 js 主线程之外开信的 worker 线程,并将 js 脚本运行其中,它赋予了开发者操作多线程的能力。当有大量运算任务时,可以把运算任务交给 WebWorker 去处理,再把结果返回主线程,这样 js 只用专注处理业务逻辑,不用耗费过多时间去处理大量复杂计算,从而减少了阻塞时间,也提高了运行效率,页面流畅度和用户体验自然而然也提高了

3 Web Worker 能干什么

虽然 Worker 线程是在浏览器环境中被唤起,但是它与当前页面窗口运行在不同的全局上下文中,我们常用的顶层对象 window,以及 parent 对象在 Worker 线程上下文中是不可用的。另外,在 Worker 线程上下文中,操作 DOM 的行为也是不可行的,document 对象也不存在。但是,location 和 navigator 对象可以以可读方式访问。除此之外,绝大多数 Window 对象上的方法和属性,都被共享到 Worker 上下文全局对象 WorkerGlobalScope 中。同样,Worker 线程上下文也存在一个顶级对象 self。

4 Web Worker 使用

4.1 创建 Web Worker
const worker = new Worker(path, options);

参数说明:

  • path:有效的 js 脚本的地址,必须遵守同源策略。无效的 js 地址或者违反同源策略,会抛出 SECURITY_ERR  类型错误

  • options.type 可选:用以指定  worker 类型。该值可以是  classic  或  module。 如未指定,将使用默认值  classic

  • options.credentials 可选:用以指定 worker 凭证。该值可以是  omit, same-origin,或  include。如果未指定,

    或者 type 是  classic,将使用默认值  omit (不要求凭证)

  • options.name:在  DedicatedWorkerGlobalScope  的情况下,用来表示 worker 的 scope 的一个  DOMString  值,主要用于调试目

4.2 主线程与 worker 线程数据传递

主线程与 worker 通过 postMesage方法进行数据传递,以及message事件来接收消息

# main.js

 const myWorker = new Worker("../js/worker.js")
    const obj = {
      name: "xmk"
    }

    myWorker.postMessage(obj)

    myWorker.addEventListener('message', e => {
      console.log("mainjs")
      // false 说明是值传递 而不是传地址
      console.log(obj === e.data)
    })

或:
    worker.onmessage = function(e) {
      console.log('Received message from worker: ' + e.data);
    };

# worker.js

self.addEventListener('message', e => {
  console.log('workerjs')
  console.log(typeof e.data)
  self.postMessage('this is workerjs')
})


或:
onmessage = function(e) {
  console.log('Received message from main script: ' + e.data);

  // 处理数据并返回
  var result = 'Processed data: ' + event.data;
  postMessage(result);
};

# 控制台输出

workerjs
object
mainjs
false

4.3 监听错误信息
# mainjs

const myWorker = new Worker('../js/worker.js'); // 创建worker

myWorker.addEventListener('error', err => {
    console.log(err.message);
});
myWorker.addEventListener('messageerror', err => {
    console.log(err.message)
});

# workerjs

self.addEventListener('error', err => {
    console.log(err.message);
});
self.addEventListener('messageerror', err => {
    console.log(err.message);
});


4.4 关闭 Worker 线程

worker 线程的关闭在主线程和 worker 线程都能进行操作,但对 worker 线程的影响略有不同。

相同点: worker 线程当前的 Event Loop 中的任务会继续执行。至于 worker 线程下一个 Event Loop 中的任务,则会被直接忽略,不会继续执行。

不同点:在主线程手动关闭 worker,主线程与 worker 线程之间的连接都会被立刻停止,即使 worker 线程当前的 Event Loop 中仍有待执行的任务继续调用 postMessage() 方法,但主线程不会再接收到消息。

在 worker 线程内部关闭 worker,不会直接断开与主线程的连接,而是等 worker 线程当前的 Event Loop 所有任务执行完,再关闭。也就是说,在当前 Event Loop 中继续调用 postMessage() 方法,主线程还是能通过监听 message 事件收到消息的。

  • mainjs

    myWorker.terminate(); // 关闭 worker

  • workerjs

    self.close(); // 直接执行 close 方法

4.4.1 主线程关闭关闭 worker
# mainjs

   const myWorker = new Worker("../js/worker.js")

    myWorker.postMessage("main send")


    myWorker.addEventListener('message', e => {
      console.log(e.data)
      myWorker.terminate();
    })

# workerjs
self.addEventListener('message', e => {
  console.log(e.data)
  postMessage('worker return')

  // 开启一个宏任务
  setTimeout(() => {
    console.log('timre start')
    postMessage('timer end')
  })
  // 开启一个微任务
  Promise.resolve().then(() => {
    console.log('Promise start')
    postMessage('Promise end')
  })

  for (let i = 0; i < 100; i++) {
    console.log('for start')
    if ((i = 100)) {
      postMessage('for end ')
    }
  }
})

# console

main send
for start
worker return
Promise start
timre start


  • 主线程只会接收到 worker 线程第一次通过 postMessage() 发送的消息,后面的消息不会接收到;
  • worker 线程当前 Event Loop 里的任务会继续执行,包括微任务
  • worker 线程里 setTimeout 创建的下一个 Event Loop 任务队列没有执行
4.4.2 worker 内部关闭
# workerjs
self.addEventListener('message', e => {
  console.log(e.data)
  postMessage('worker return')
  self.close()
  ... ...


# console

main send
for start
Promise start
worker return
for end
Promise end

  • worker 线程当前的 Event Loop 任务队列中的 postMessage() 事件都会被主线程监听到

 

4.5 Worker 线程引入其他 js 对象

使用importScript()方法

# utils.js
const add = (a,b) => { a + b }

# worker.js
importScript('./utils.js')
console.log(add(3,4))

4.6 Wroker 的 ESMoudle 模式
# main.js

const worker = new worker('./js/worker.js',{
  type : 'module'
})

# utils.js

export default add = (a,b)=>a + b

# worker.js

import add from './utils.js'
add(1,2)

4.7 Worker 和主线程可以传递的数据

postMessage() 传递的数据可以是由结构化克隆算法处理的任何值或 JavaScript 对象,包括循环引用。

结构化克隆算法不能处理的数据:

  • Error 以及 Function 对象
  • DOM 节点
  • 对象的某些特定参数不会被保留
    • RegExp 对象的 lastIndex 字段不会被保留
    • 属性描述符,setters 以及 getters(以及其他类似元数据的功能)同样不会被复制。例如,如果一个对象用属性描述符标记为 read-only,它将会被复制为 read-write
    • 原形链上的属性也不会被追踪以及复制。

结构化算法支持的数据:

  • 所有的原始数据类型(Symbol 除外)
  • Boolean 对象
  • String 对象
  • Date
  • RegExp ( lastIndex 字段不会被保留。)
  • Blob[Blob - Web API 接口参考 | MDN]
  • File
  • FileList
  • ArrayBuffer
  • ArrayBufferView
  • ImageData
  • Array
  • Object
  • Map
  • Set

5 SharedWorker

SharedWorker 是一种特殊类型的 Worker,可以被多个浏览上下文访问,比如多个 windows,iframes 和 workers,但这些浏览上下文必须同源。它们实现于一个不同于普通  worker 的接口,具有不同的全局作用域:SharedWorkerGlobalScope ,但是继承自 WorkerGlobalScope

6 使用

# main.js
   const worker = new Worker('../js/calc.js')

    function calc() {
      const num = parseInt(document.getElementById('num').value)
      worker.postMessage(num)
    }

    worker.onmessage = function (e) {
      document.getElementById('result').innerHTML = e.data
    }
    
  
# calc.js

function calc(num) {
  let result = 0
  let startTime = performance.now()
  // 计算求和(模拟复杂计算)
  for (let i = 0; i <= num; i++) {
    result += i
  }
  // 由于是同步计算,在没计算完成之前下面的代码都无法执行
  const time = performance.now() - startTime
  console.log('总计算花费时间:', time)
  self.postMessage(result)
}

self.onmessage = function (e) {
  calc(e.data)
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值