别再让 JavaScript 卡死页面!Web Workers 零基础上手指南

一、为什么需要多线程?

1.1 JavaScript的单线程困境

javascript 复制代码
// 主线程中的复杂计算会阻塞UI
function calculatePrimes(max) {
  const primes = [];
  for (let i = 2; i <= max; i++) {
    let isPrime = true;
    for (let j = 2; j < i; j++) {
      if (i % j === 0) {
        isPrime = false;
        break;
      }
    }
    if (isPrime) primes.push(i);
  }
  return primes;
}

// 点击按钮后界面会无法操作一段时间
document.getElementById('calculate').addEventListener('click', () => {
  const primes = calculatePrimes(100000); // 阻塞主线程
  document.getElementById('result').textContent = `找到 ${primes.length} 个质数`;
});

1.2 Web Workers的核心优势

  • 并行执行:在后台线程运行脚本
  • 非阻塞UI:保持主线程响应性
  • 隔离环境:Worker有自己的全局作用域
  • 充分利用多核CPU:现代浏览器支持多Worker并行

二、创建你的第一个Web Worker

2.1 基本结构

html 复制代码
<!-- index.html -->
<button id="start">开始计算</button>
<div id="result"></div>

<script>
  const worker = new Worker('worker.js');
  
  document.getElementById('start').addEventListener('click', () => {
    worker.postMessage({ command: 'calculate', max: 100000 });
  });
  
  worker.onmessage = (event) => {
    document.getElementById('result').textContent = 
      `找到 ${event.data.count} 个质数`;
  };
</script>
javascript 复制代码
// worker.js
self.onmessage = (event) => {
  if (event.data.command === 'calculate') {
    const primes = calculatePrimes(event.data.max);
    self.postMessage({ count: primes.length });
  }
};

function calculatePrimes(max) {
  // 同前的质数计算逻辑
}

三、高效数据传递

3.1 结构化克隆算法

javascript 复制代码
// 主线程
const complexData = {
  array: new Float32Array(1000),
  date: new Date(),
  nested: { value: '可克隆对象' }
};

worker.postMessage(complexData);

// Worker中
self.onmessage = (event) => {
  console.log(event.data.date.getFullYear()); // 正确访问
};

3.2 传输大型数据(零拷贝)

javascript 复制代码
// 传输ArrayBuffer而非复制
const buffer = new ArrayBuffer(1024 * 1024 * 100); // 100MB

// 传统方式(复制数据)
worker.postMessage({ buffer });

// 高效方式(转让所有权)
worker.postMessage({ buffer }, [buffer]);

// 主线程中buffer现在不可用
console.log(buffer.byteLength); // 0

3.3 双向通信模式

javascript 复制代码
// 主线程
worker.postMessage({ type: 'start', max: 1000000 });
worker.onmessage = (event) => {
      if (event.data.type === 'progress') {
        console.log(event.data.value);
      }
};

// Worker中
self.onmessage = (event) => {
  console.log(event.data); // 正确访问
  self.postMessage({ type: 'progress', value: 0.1 });
};

总结

如果你喜欢本教程,记得点赞+收藏!关注我获取更多JavaScript开发干货。

相关推荐
啃火龙果的兔子1 小时前
修改 Lucide-React 图标样式的方法
前端·react.js·前端框架
前端 贾公子1 小时前
为何在 Vue 的 v-model 指令中不能使用可选链(Optional Chaining)?
前端·javascript·vue.js
潘多拉的面1 小时前
Vue的ubus emit/on使用
前端·javascript·vue.js
遗憾随她而去.2 小时前
js面试题 高频(1-11题)
开发语言·前端·javascript
hqxstudying4 小时前
J2EE模式---前端控制器模式
java·前端·设计模式·java-ee·状态模式·代码规范·前端控制器模式
开开心心就好5 小时前
Excel数据合并工具:零门槛快速整理
运维·服务器·前端·智能手机·pdf·bash·excel
im_AMBER6 小时前
Web开发 05
前端·javascript·react.js
Au_ust6 小时前
HTML整理
前端·javascript·html
安心不心安6 小时前
npm全局安装后,依然不是内部或外部命令,也不是可运行的程序或批处理文件
前端·npm·node.js
迷曳8 小时前
28、鸿蒙Harmony Next开发:不依赖UI组件的全局气泡提示 (openPopup)和不依赖UI组件的全局菜单 (openMenu)、Toast
前端·ui·harmonyos·鸿蒙