跳转至

JavaScript 多任务异步执行详解

本文档详细介绍了如何使用 Web Worker 和 Promise 实现多任务的异步执行,以提高应用程序的性能和响应速度。

Web Worker 实现多任务异步执行

Web Worker 允许在后台线程中运行脚本,从而避免阻塞主线程,特别适合处理计算密集型任务。

概述

Web Worker 实现多任务异步执行,通过将任务分配给多个 Web Worker,可以有效地利用多核 CPU 提升任务处理效率,并保持主线程的响应性。

代码结构

  • worker.js:定义了每个 Web Worker 的逻辑,用于处理分配给它的子任务
  • main.ts:主线程代码,负责创建和管理多个 Web Worker,并向它们发送任务数据

实现步骤

1. 创建 Web Worker

在 main.ts 中,首先定义一个 workers 数组来存储所有的 Web Worker 实例,并设置 workerCount 变量来跟踪剩余的 Worker 数量。

const workers: Worker[] = [];
let workerCount = 10; // Worker 的数量

2. 初始化计时

记录开始时间,以便后续计算总耗时。

const startTime = Date.now();

3. 创建并启动 Web Worker

使用 for 循环创建指定数量的 Web Worker,并为每个 Worker 设置消息接收和错误处理事件监听器。

for (let i = 0; i < workerCount; i++) {
  const worker = new Worker(new URL('./worker.js', import.meta.url), { type: 'module' });
  workers.push(worker);

  // 监听 Worker 发送的消息
  worker.onmessage = function(event) {
    console.log('从 Worker 收到的消息:', event.data);
    workerCount--;

    if (workerCount === 0) {
      console.log('所有 Worker 已经完成任务。');
      const endTime = Date.now();
      console.log(`总耗时: ${endTime - startTime} 毫秒`);
      workers.forEach(worker => worker.terminate());
    }
  };

  // 监听 Worker 错误
  worker.onerror = function(error) {
    console.error('Worker 错误:', error);
    workerCount--;

    if (workerCount === 0) {
      console.log('所有 Worker 已经完成任务。');
      const endTime = Date.now();
      console.log(`总耗时: ${endTime - startTime} 毫秒`);
      workers.forEach(worker => worker.terminate());
    }
  };

  // 发送数据给 Web Worker
  worker.postMessage({
    data: `Hello, Worker!`,
    index: i,
  });
}

4. Web Worker 处理逻辑

在 worker.js 中,每个 Web Worker 接收到主线程发送的数据后,会模拟一个耗时任务(使用 setTimeout),并在任务完成后向主线程发送消息。

self.onmessage = function(event) {
  const { data, index } = event.data;
  const start = Date.now();
  console.log('接收到数据', data);
  console.log('开始执行子任务', index);

  setTimeout(() => {
    console.log(`子任务${index}结束,耗时:`, Date.now() - start);
    self.postMessage({ data });
  }, index * 500);
};

运行结果

每个 Web Worker 在接收到任务后会立即开始执行,并在指定的时间间隔后完成任务。主线程会在所有 Web Worker 完成任务后输出总耗时,并终止所有 Web Worker。

注意事项

  1. 确保 worker.js 和 main.ts 文件路径正确
  2. Web Worker 的数量可以根据实际需求进行调整
  3. 在生产环境中,建议对 Web Worker 的通信和错误处理进行更详细的日志记录和异常处理

Promise 实现多任务异步执行

Promise 提供了一种更简洁的方式来处理异步操作,特别适合处理 I/O 操作等非计算密集型任务。

概述

使用 Promise 实现多任务异步执行,可以更方便地处理多个异步任务,并在所有任务完成后执行特定的操作。

实现步骤

1. 创建多个 Promise

在 main.ts 中,创建一个包含多个 Promise 的数组,每个 Promise 模拟一个耗时任务。

const tasks = [];
let taskCount = 10; // 任务的数量

const startTime = Date.now();

for (let i = 0; i < taskCount; i++) {
  const task = new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(`任务${i}结束,耗时:`, Date.now() - startTime);
      resolve(`任务${i}完成`);
    }, i * 500);
  });
  tasks.push(task);
}

2. 使用 Promise.all 处理所有 Promise

使用 Promise.all 方法可以等待所有 Promise 完成,并在所有任务完成后执行特定的操作。

Promise.all(tasks)
  .then(results => {
    console.log('所有任务已经完成。');
    console.log('任务结果:', results);
    const endTime = Date.now();
    console.log(`总耗时: ${endTime - startTime} 毫秒`);
  })
  .catch(error => {
    console.error('任务错误:', error);
  });

运行结果

每个 Promise 在接收到任务后会立即开始执行,并在指定的时间间隔后完成任务。主线程会在所有 Promise 完成任务后输出总耗时,并显示所有任务的结果。

注意事项

  1. 确保每个 Promise 的逻辑正确,并且能够正确地 resolve 或 reject
  2. 在生产环境中,建议对 Promise 的错误处理进行更详细的日志记录和异常处理

对比与选择

Web Worker 的优势

  • 多线程处理:Web Worker 可以在独立的线程中执行任务,不会阻塞主线程,适合处理耗时的计算密集型任务
  • 资源隔离:每个 Web Worker 都有自己的全局作用域,避免了主线程和 Worker 之间的变量冲突
  • 更好的性能:通过利用多核 CPU,可以显著提高任务处理效率

Promise 的优势

  • 简洁的语法:使用 Promise 可以更简洁地处理多个异步任务,避免了回调地狱
  • 更好的可读性:Promise 的链式调用使得代码更易读和维护
  • 错误处理:Promise 提供了更方便的错误处理机制,可以通过 .catch 方法统一处理错误

选择合适的方案

  • Web Worker:适用于需要在独立线程中执行的耗时任务,特别是计算密集型任务
  • Promise:适用于需要在主线程中处理的异步任务,特别是 I/O 操作或其他非阻塞任务

通过合理选择和结合使用 Web Worker 和 Promise,可以更好地利用 JavaScript 的异步特性,提高应用程序的性能和响应速度。