5.实现并发任务

书诚小驿2025/03/09前端面经Node

一、基础方案:直接并发(无限制)

使用 Promise.allPromise.allSettled 直接触发多个异步任务,但不控制并发数量,适用于少量任务或浏览器自动处理并发限制的场景。

// 示例:同时发起多个请求(无并发控制)
async function runConcurrentTasks(tasks) {
  const results = await Promise.all(tasks.map((task) => task()));
  return results;
}

// 使用
const tasks = [fetchData1, fetchData2, fetchData3];
runConcurrentTasks(tasks).then((results) => {
  console.log("所有任务完成:", results);
});
  • 一次性触发所有任务,由浏览器底层控制实际并发数(如 HTTP/1.1 同一域名下默认最多 6 个并发)。

  • 若任务数量过多,可能导致性能问题或请求被阻塞。

二、手动控制并发数量,使用队列

通过队列和计数器限制同时运行的异步任务数量,避免资源耗尽。

  1. 实现一个并发控制器
class ConcurrencyController {
  constructor(maxConcurrency) {
    this.maxConcurrency = maxConcurrency; // 最大并发数
    this.taskQueue = []; // 任务队列
    this.runningCount = 0; // 当前运行中的任务数
  }

  // 添加任务到队列
  addTask(task) {
    return new Promise((resolve, reject) => {
      this.taskQueue.push({ task, resolve, reject });
      this._run();
    });
  }

  // 执行任务(内部方法)
  _run() {
    while (
      this.runningCount < this.maxConcurrency &&
      this.taskQueue.length > 0
    ) {
      const { task, resolve, reject } = this.taskQueue.shift();
      this.runningCount++;
      task()
        .then(resolve)
        .catch(reject)
        .finally(() => {
          this.runningCount--;
          this._run(); // 递归触发下一个任务
        });
    }
  }
}
  1. 使用示例
// 初始化控制器(最大并发数=2)
const controller = new ConcurrencyController(2);

// 模拟异步任务(如API请求)
function createTask(id, delay) {
  return () =>
    new Promise((resolve) => {
      setTimeout(() => {
        console.log(`任务 ${id} 完成`);
        resolve(id);
      }, delay);
    });
}

// 添加5个任务(每个任务耗时不同)
const tasks = [
  createTask(1, 1000),
  createTask(2, 500),
  createTask(3, 800),
  createTask(4, 300),
  createTask(5, 1200),
];

// 并发执行任务(最多同时2个)
tasks.forEach((task) => {
  controller.addTask(task).then((result) => {
    console.log("任务结果:", result);
  });
});

// 输出:
// 任务 2 完成 (500ms后)
// 任务 4 完成 (300ms后,任务3被提前触发)
// 任务 3 完成 (800ms后)
// 任务 1 完成 (1000ms后)
// 任务 5 完成 (1200ms后)
  • 精准控制并发数(如限制为 2,避免资源过载)。

  • 任务按添加顺序启动,但完成顺序取决于执行时间。

  • 自动排队,确保始终不超过最大并发数。

三、高级场景:动态优先级 & 错误处理

扩展控制器以支持任务优先级和错误重试机制。

  1. 实现一个优先级队列
class AdvancedConcurrencyController extends ConcurrencyController {
  addTask(task, priority = 0) {
    return new Promise((resolve, reject) => {
      // 根据优先级插入队列(priority值越小优先级越高)
      const queueItem = { task, resolve, reject, priority };
      let index = this.taskQueue.findIndex((item) => item.priority > priority);
      index = index === -1 ? this.taskQueue.length : index;
      this.taskQueue.splice(index, 0, queueItem);
      this._run();
    });
  }
}

// 使用示例
const advancedController = new AdvancedConcurrencyController(2);
advancedController.addTask(createTask("高优先级", 500), 1);
advancedController.addTask(createTask("低优先级", 1000), 2);
  1. 错误重试
class RetryConcurrencyController extends ConcurrencyController {
  constructor(maxConcurrency, maxRetries = 3) {
    super(maxConcurrency);
    this.maxRetries = maxRetries;
  }

  async _executeTaskWithRetry(task, resolve, reject) {
    let retries = 0;
    const execute = async () => {
      try {
        const result = await task();
        resolve(result);
      } catch (error) {
        if (retries < this.maxRetries) {
          retries++;
          console.log(`任务重试中(第${retries}次)`);
          await execute();
        } else {
          reject(error);
        }
      }
    };
    await execute();
  }

  _run() {
    // 重写 _run 方法,替换任务执行逻辑
    while (
      this.runningCount < this.maxConcurrency &&
      this.taskQueue.length > 0
    ) {
      const { task, resolve, reject } = this.taskQueue.shift();
      this.runningCount++;
      this._executeTaskWithRetry(task, resolve, reject).finally(() => {
        this.runningCount--;
        this._run();
      });
    }
  }
}

四、适用场景

场景技术方案说明
分页数据预加载基础并发控制(如并发数=3)同时加载多个分页数据,提升用户体验。
大文件分片上传高级并发控制器 + 优先级分片并行上传,优先传输关键分片。
批量图片懒加载基础并发控制(如并发数=2)限制同时加载的图片数量,避免阻塞页面渲染。
实时数据轮询动态优先级队列确保重要数据的轮询请求优先执行。
接口请求失败重试错误重试控制器对失败请求自动重试,提高可靠性。
最后更新时间' 2025/3/9 18:24:05