WebWorker——JavaScript也可以“多线程”

简介: 虽然JavaScript是单线程的,但是浏览器是多线程的啊,我们可以借助浏览器再跑一个JavaScript线程来帮助主JavaScript线程减轻压力,JavaScript之所以是单线程的原因是要保持数据的一致性,防止多个线程修改数据导致数据不一致,所以虽然可以使用worker,但也要遵循一些规范

前言


这里不要被标题误导,我打引号了,JavaScript并不能实现多线程(至少到目前为止,未来应该也不会)。

虽然JavaScript是单线程的,但是浏览器是多线程的啊,我们可以借助浏览器再跑一个JavaScript线程来帮助主JavaScript线程减轻压力,JavaScript之所以是单线程的原因是要保持数据的一致性,防止多个线程修改数据导致数据不一致,所以虽然可以使用worker,但也要遵循一些规范

  • 同源限制:分配给 Web Worker 线程运行的脚本文件,必须与主线程的脚本文件同源
  • DOM 限制:Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用 document、window、parent 这些对象。但是,Worker 线程可以访问 navigator 对象和 location 对象。
  • 通信联系:Worker 线程和主线程不再同一个上下文环境,它们不能直接通信,必须通过消息完成。
  • 脚本限制:Worker 线程不能执行 alert()confirm() 方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求
  • 文件限制:Worker 线程无法读取本地文件,即不能打开本地的文件系统(file://) ,它所加载的脚本,必须来自网络


创建worker


在使用worker时我们需要使用构造方法Worker实例化一个worker实例,传入worker文件的地址或者使用字符串转URL,例如

// 方法一
const worker = new Worker('./worker.js')
// 方法二
const code = `
  // worker's code
`
const buffer = new Blob([code])
const url = URL.createObjectURL(buffer)
const worker = new Worker(url)
复制代码

创建worker对象后:

  • 使用postMessage方法可以向worker发送消息,消息可以是任何类型,包括二进制;
  • 使用onmessage方法用来监听worker的消息;
  • 使用onerror方法用来监听worker线程的报错;
  • 使用terminate方法用来结束worker线程。
function startWorker() {
  const worker = new Worker('./worker.js')
  worker.postMessage({ x: 1, y: 2 })
  worker.onmessage = ({ data }) => {
    alert(data.data);
  }
  worker.onerror = (e) => {
    console.log(`[ERROR: Line ${e.lineno}] in ${e.filename}: ${e.message}`);
  }
}
复制代码

worker内,通过self来访问worker自身全局,

  • 使用onmessage方法处理主进程中通过postMessage方法触发的事件;
  • 使用postMessage方法向主线程发送数据,同样可以是任何数据类型;
  • worker线程中也可以引入其他js文件,使用importScripts(path)来引入;
  • worker可以使用close方法来主动关闭线程。
self.onmessage = ({ data }) => {
  // 一般使用worker来处理耗时任务,所以使用定时器模拟
  setTimeout(() => {
    self.postMessage({ data: data.x + data.y })
    throw new Error('抛出异常')
    self.close()
  }, 5000)
}
复制代码

以上类似onmessage的形式,均可以通过addEventListener来代替


实战练习


任务说明

在HTML页面中通过按钮触发worker事件:运算、退出……

main.js

const worker = new Worker('./worker.js')
worker.addEventListener('message', ({ data }) => {
  console.log(data);
})
worker.addEventListener('error', (e) => {
  console.log(`[ERROR: Line ${e.lineno}] in ${e.filename}: ${e.message}`);
})
function cal() {
  const num1 = document.getElementById('num1').value
  const num2 = document.getElementById('num2').value
  worker.postMessage({ cmd: 'cal', data: { num1, num2 } })
}
function exit() {
  worker.postMessage({ cmd: 'exit' })
  // 或者worker.terminate()
}
复制代码

Worker.js

self.addEventListener('message', ({ data }) => {
  switch (data.cmd) {
    case 'cal':
      const result = Number(data.data.num1) + Number(data.data.num2)
      if (isNaN(result)) {
        throw new Error('Input Type Error')
      } else {
        self.postMessage(result)
      }
      break
    case 'exit':
      self.close()
      break
    default:
      self.postMessage('Unknown command')
  }
})
复制代码

index.html

<body>
  num1: <input type="text" id="num1" />
  num2: <input type="text" id="num2" />
  <button type="button" onclick="cal()">计算</button>
  <button type="button" onclick="exit()">关闭</button>
</body>
复制代码

效果演示:

1682519770(1).png


应用场景


WebWorker 带来后台计算能力,WebWorker 自身是由 Webkit 多线程实现,但它并没有为 JavaScript 语言带来多线程编程特性,我们现在仍然不能在 JavaScript 代码中创建并管理一个线程,或者主动控制线程间的同步与锁等特性。Web Worker 只是浏览器(宿主环境)提供的一个能力 / API。而且它不支持 IE。

使用专用线程进行数学运算Web Worker 最简单的应用就是用来做后台计算,而这种计算并不会中断前台用户的操作

图像处理通过使用从 Canvas 或者 Video 元素中获取的数据,可以把图像分割成几个不同的区域并且把它们推送给并行的不同 Workers 来做计算

大量数据的检索当需要在调用 AJAX 后处理大量的数据,如果处理这些数据所需的时间长短非常重要,可以在 Web Worker 中来做这些,避免干扰 UI 线程。

背景数据分析由于在使用 Web Worker 的时候,我们有更多潜在的 CPU 可用时间,我们现在可以考虑一下 JavaScript 中的新应用场景。我们现在可以考虑一下 JavaScript 中的新应用场景。例如,我们可以想像在不影响 UI 体验的情况下实时处理用户输入。利用这样一种可能,我们可以想像一个像 Word(Office Web Apps 套装)一样的应用:当用户打字时后台在词典中进行查找,帮助用户自动纠错等等。

相关文章
|
4月前
|
数据采集 并行计算 JavaScript
实战指南:在 Node.js 中利用多线程提升性能
在 Node.js 的世界中,多线程技术一直是一个受到广泛关注的领域。最初,Node.js 设计为单线程模式。随着技术发展,Node.js 引入了多线程支持,进而利用多核处理器的强大性能,提升了应用性能。接下来的内容将深入探讨 Node.js 如何实现多线程,以及在何种场合应该采用这种技术。
|
13天前
|
Web App开发 JavaScript 前端开发
[译] 深入理解 Node.js 中的 Worker 线程
[译] 深入理解 Node.js 中的 Worker 线程
|
3月前
|
前端开发 JavaScript
JavaScript异步处理避免了单线程阻塞,如回调函数、Promise和async/await。
【6月更文挑战第22天】JavaScript异步处理避免了单线程阻塞,如回调函数、Promise和async/await。回调是基础,用于在操作完成后执行函数;Promise管理异步状态,支持链式调用;async/await提供同步代码外观,简化错误处理。每种技术在处理耗时任务时都起着关键作用。
29 3
|
4月前
|
移动开发 JavaScript 前端开发
Web Worker:JavaScript的后台任务解决方案
Web Worker:JavaScript的后台任务解决方案
|
4月前
|
JavaScript 前端开发
JS 单线程还是多线程,如何显示异步操作
JS 单线程还是多线程,如何显示异步操作
41 2
|
4月前
|
移动开发 JavaScript 前端开发
【JavaScript技术专栏】Web Worker在JavaScript中的应用
【4月更文挑战第30天】HTML5的Web Worker API解决了JavaScript单线程性能瓶颈问题,允许在后台线程运行JS代码。本文介绍了Web Worker的基本概念、类型、用法和应用场景,如复杂计算、图像处理和数据同步。通过实例展示了搜索建议、游戏开发和实时数据分析等应用,并提醒注意其无法直接访问DOM、需消息传递通信以及移动端资源管理。Web Worker为前端开发提供了多线程能力,提升了Web应用性能和用户体验。
58 0
|
4月前
|
消息中间件 前端开发 JavaScript
JavaScript 线程:处理高并发任务的必备知识(下)
JavaScript 线程:处理高并发任务的必备知识(下)
JavaScript 线程:处理高并发任务的必备知识(下)
|
4月前
|
前端开发 JavaScript UED
JavaScript 线程:处理高并发任务的必备知识(上)
JavaScript 线程:处理高并发任务的必备知识(上)
JavaScript 线程:处理高并发任务的必备知识(上)
|
4月前
|
消息中间件 JavaScript 前端开发
Node.js 中的线程 与 并发
Node.js 中的线程 与 并发
43 0
|
4月前
|
前端开发 JavaScript
异步编程:由于JS是单线程执行的,所以对于耗时的操作(如网络请求),需要通过异步编程来处理。回调函数、Promise、async/await都是常用的异步编程方式。
异步编程:由于JS是单线程执行的,所以对于耗时的操作(如网络请求),需要通过异步编程来处理。回调函数、Promise、async/await都是常用的异步编程方式。
82 1