javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > JS webworker详解

JS中webworker的作用与实操示例详解

作者:搞前端的小王

WebWorker是HTML5新引入的一种技术,旨在为Web内容在后台运行脚本提供一种简单的方法,这篇文章主要介绍了JS中webworker的作用与实操的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

为什么需要webworker

JavaScript 需要引入 ‌Web Worker‌ 的核心原因源自其‌单线程模型的局限性‌。以下是关键原因及技术价值分析:

一、突破单线程阻塞瓶颈

1.JS 单线程的本质

JavaScript 设计之初基于单线程模型,主线程需同时处理 ‌UI 渲染、事件响应、逻辑计算‌等任务。若执行耗时操作(如大数据计算),会导致页面卡顿甚至无响应,用户体验急剧下降‌。Web 2.Worker 的解决方案

创建独立的后台线程运行脚本,将复杂任务剥离主线程,避免阻塞关键渲染流程‌

二、释放多核 CPU 性能潜力

‌1.并行计算能力

Web Worker 允许创建多个子线程,将任务分片并行处理(如图像滤镜、科学计算),充分利用多核 CPU 硬件资源,显著提升处理效率‌

2.资源分配优化

主线程专注 UI 交互与渲染,Worker 线程处理计算密集型任务,实现计算资源合理分配‌

三、增强应用稳定性与体验

1.崩溃隔离机制

Worker 线程运行在独立环境中,即使子线程崩溃或报错,也不会影响主线程及其他 Worker 的稳定性‌

2.流畅的用户交互保障

例如实时金融图表渲染场景,主线程仅负责接收结果并渲染,确保用户操作无延迟‌

webworker的原理

Web Worker 是 JavaScript 实现多线程的核心机制,其原理涉及底层线程模型、通信机制和环境隔离等多个关键技术点。

核心工作机制详解

1. ‌独立线程模型‌

2. ‌线程间通信机制

// 主线程

const worker = new Worker('worker.js');
worker.postMessage(data); // 发送数据
// Worker 线程 (worker.js)
self.onmessage = (e) => {
        const result = process(e.data); self.postMessage(result); // 返回结果
}

3. ‌事件循环隔离‌

特性主线程Web Worker
事件循环UI渲染+JS执行纯JS执行环境
阻塞影响导致页面冻结仅影响自身线程
任务优先级高(UI响应优先)低(计算任务优先)

4. ‌资源访问限制‌

Web Worker ‌无法访问‌:

但‌可以访问‌:

5. ‌线程生命周期管理‌

// 创建Worker
const worker = new Worker('worker.js');
// 终止Worker (立即停止)
worker.terminate();
// Worker内部自终止
self.close();

底层实现差异(浏览器引擎)

浏览器引擎线程模型实现特点
ChromiumBlink渲染引擎 + V8隔离实例每个Worker独立V8实例
FirefoxSpiderMonkey独立上下文共享JIT编译器但隔离堆栈
SafariJavaScriptCore独立虚拟机精细内存管理

性能优化要点

  1. 线程复用‌:避免频繁创建/销毁 (线程池模式)
  2. 批量通信‌:合并多次消息传递
  3. 高效数据结构‌:优先使用TypedArray
  4. 负载均衡‌:根据CPU核心数动态分配任务
  5. 超时控制‌:防止僵尸线程

Web Worker 通过线程隔离和高效通信机制,在保持JavaScript单线程编程模型的同时,实现了真正的并行计算能力,成为现代Web应用性能优化的关键基础设施。

webworker典型应用场景

场景类型实例技术价值
大数据处理实时日志分析/金融统计避免遍历海量数据阻塞 UI‌27
音视频处理4K 视频解码/Canvas 滤镜分离计算与渲染流程‌17
高频交互优化游戏物理引擎/传感器数据处理减少主线程压力‌25
预加载与缓存SPA 资源预载入结合 Service Worker 提升加载速度‌

webworker实操

写在前面:为什么主线程执行大量的同步运算会导致页面卡顿?

浏览器默认会有一个刷新频率,这和个人电脑的默认刷新率有关,一般默认为60HZ,也就是浏览器页面会一秒钟刷新60次,计算下来差不多每16ms就要刷新一次,才能保证页面上所有的样式和布局以及DOM节点信息是最新的,如果主线程此时运行大量运算,超过16ms,就会造成渲染阻塞。这就是需要用webworker分摊主线程大量运算的原因,至于每个webworker运行多久不需要我们担心,因为它在其他线程上,每个worker都是一个新线程,它不在js主线程上,它的运行不会阻塞页面渲染。

细节点补充:

1.如何查看自己电脑上的实际cpu核数与逻辑处理器核数

任务管理器>性能面板>内核数与逻辑处理器数(一般逻辑处理器数会比真实内核数要多)

2.电脑屏幕刷新率查看:

设置>系统>屏幕>高级显示器设置>刷新率

因为webworker属于web API,所以必须要运行在浏览器环境下,需要通过live-server插件运行示例html或者将相关代码放置在一个vue或react模板工程中执行

index.html内容

<!doctype html>
<html lang="en">
  <body>
    <div id="app"></div>
    <!-- 启用webworker效果 -->
     <script src="fiberMain.js"></script>
     <!-- 不启用webworker效果 -->
     <!-- <script src="fiberWithout.js"></script> -->
  </body>
</html>

fiberMain.js 

// 主线程代码,引用fiberWorker.js作为worker线程执行代码,实现大量cpu密集型运算从主线程中拆分至worker线程中。

const startTime = Date.now();
// 动态启用本机电脑上的cpu逻辑处理器核数并发执行多个线程
const workCount = navigator.hardwareConcurrency || 4;
const fiberArr = Array(50).fill().map((_, index)=> 30 + index);
// 创建多个worker示例
const workers = Array(workCount).fill().map(() => new Worker('./fiberWorker.js'))
const chunkSize = Math.ceil(fiberArr.length / workCount);
// 任务切片分配给各个worker
workers.forEach((worker, i) => {
  const start = i * chunkSize;
  const chunk = fiberArr.slice(start, start + chunkSize);
  worker.postMessage({chunk, id : i});
  worker.onmessage = (e) => {
    // 每个worker运算结束后worker的单独耗时
    console.log('每一个worker计算结果结束完成后经过的毫秒数::', (Date.now() - startTime));
  }
  worker.onerror = function (err) {
    console.error(err)
  }
})
// 主线程代码执行到这里的耗时
console.log('主线程同步代码执行完成后的耗时毫秒数:::', (Date.now() - startTime))

fiberWorker.js   worker线程所要执行的逻辑代码,需要和fibermain.js搭配使用

// worker计算逻辑,斐波那契递归数列计算
self.onmessage = (e) => {
  const res = e.data.chunk.map(n => fibonacci(n))
  self.postMessage({res, id: e.data.id})
}
// 斐波那契计算函数(模拟耗时操作)
function fibonacci(n) {
  if (n <= 1) return n; // 基线条件:fib(0)=0, fib(1)=1
  return fibonacci(n - 1) + fibonacci(n - 2); // 递归调用
}

fiberWithout.js 

//  无worker线程,大量的递归运算直接运行在主线程中的写法,主线程同步执行大量运算导致耗时很长,大概2000ms,会严重阻塞每16ms就要执行一次的页面渲染,导致页面和用户交互异常卡顿。

//  不用worker,主线程单线程计算
const startTime = Date.now();
const fiberArr = Array(22).fill().map((_, index)=> 20 + index);
function fibonacci(n) {
  return n <= 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
}
const res = fiberArr.map(n => fibonacci(n));
console.log('同步计算的最终结果::', res);
console.log('主线程同步计算的耗时毫秒数:::', (Date.now() - startTime));

fiberWithout.js 代码在主线程上运行相同的斐波那契数列计算逻辑耗时2013ms

fiberMain.js 配合 fiberWorker.js 在主线程上运行相关代码大大缩短同步代码等待执行时间,因为把大量的cpu密集型(递归回调)运算放置在了多个worker中运算,所以主线程的同步代码执行很快,耗时2ms,但是这并不意味这worker内单个线程中的运算很快速,实际每个worker运算耗时加着线程间的通信,单个worker最长执行总耗时2064ms。

为什么要启用worker,因为js主线程是单线程,js执行与页面渲染全在主线程上执行,js同步代码执行过长会导致渲染阻塞,导致页面卡顿,如果把上面的运算逻辑放在主线程中执行(也就是fiberWithout.js这种写法),那么会导致页面有2000多ms的渲染阻塞,因为js主线程需要每16ms就要运行一次页面渲染逻辑。所以会导致页面卡顿,造成页面渲染不及时以及用户交互得不到及时响应。

把大量的运算分散到多个worker中,相当于把主线程的大量运算分摊到了其他线程中,缩减主线程同步代码至2ms,不会造成页面卡顿。至于每个worker运行2000ms那不是我们关心的重点,worker就是用来分摊主线程的运算负担的,防止页面卡顿。

webworker技术限制与注意事项

1.无法操作 DOM

Worker 线程无法访问 windowdocument 等对象,需通过 postMessage 与主线程通信‌

2.通信成本控制

频繁跨线程传递大数据(如图像二进制流)可能抵消性能优势,需采用 Transferable Objects 优化

// 高效传递 ArrayBuffer
worker.postMessage(buffer, [buffer]); 

总结

到此这篇关于JS中webworker的作用与实操的文章就介绍到这了,更多相关JS webworker详解内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文