JavaScript

梗概

Promise 的链式调用特性可以用来实现一个简单的任务队列,让异步任务按顺序一个接一个地执行,避免回调地狱。

实现原理

Promise 链式调用的特点:

  1. 每个 Promise 的 then/catch 方法都会返回一个新的 Promise
  2. 后续 Promise 会等待前一个 Promise 解决后才执行
  3. 可以通过不断链接 then 方法来构建顺序执行的任务

实现方式

基本任务队列

// 创建一个初始 Promise
let taskQueue = Promise.resolve();
 
// 添加任务到队列
function addTask(task) {
  taskQueue = taskQueue.then(() => task());
  return taskQueue; // 返回任务队列以便链式调用
}
 
// 使用示例
addTask(() => console.log("Task 1"));
addTask(() => new Promise(resolve => {
  setTimeout(() => {
    console.log("Task 2");
    resolve();
  }, 1000);
}));
addTask(() => console.log("Task 3"));
 
// 输出:
// Task 1
// (1秒后) Task 2
// Task 3

带有结果收集的任务队列

class TaskQueue {
  constructor() {
    this.queue = Promise.resolve();
    this.results = [];
  }
 
  addTask(task) {
    this.queue = this.queue.then(async () => {
      const result = await task();
      this.results.push(result);
      return result;
    }).catch(error => {
      console.error("Task failed:", error);
      this.results.push(null); // 或者其他错误标识
      // 不抛出错误,继续执行后续任务
    });
    return this;
  }
 
  async runAll() {
    await this.queue;
    return this.results;
  }
}
 
// 使用示例
const queue = new TaskQueue();
queue.addTask(() => Promise.resolve("Result 1"))
     .addTask(() => new Promise(resolve => setTimeout(() => resolve("Result 2"), 1000)))
     .addTask(() => "Result 3");
 
queue.runAll().then(results => {
  console.log("All tasks completed with results:", results);
});

带控制流的任务队列

class AdvancedTaskQueue {
  constructor() {
    this.queue = Promise.resolve();
    this.paused = false;
    this.tasks = [];
  }
 
  addTask(task) {
    this.tasks.push(task);
    if (!this.paused) {
      this._runNext();
    }
    return this;
  }
 
  _runNext() {
    if (this.tasks.length === 0 || this.paused) return;
    
    const task = this.tasks.shift();
    this.queue = this.queue.then(() => {
      return task();
    }).then(() => {
      if (!this.paused) {
        this._runNext();
      }
    }).catch(error => {
      console.error("Task error:", error);
      if (!this.paused) {
        this._runNext();
      }
    });
  }
 
  pause() {
    this.paused = true;
    return this;
  }
 
  resume() {
    if (this.paused) {
      this.paused = false;
      this._runNext();
    }
    return this;
  }
 
  clear() {
    this.tasks = [];
    return this;
  }
}

应用场景

  • 顺序处理 API 请求
  • 文件上传队列
  • 动画序列
  • 复杂的初始化流程
  • 数据处理流水线

注意事项

  • 单一错误不会中断整个队列,但需要适当处理错误
  • 大量任务可能导致内存占用,考虑分批执行
  • 任务队列应该有暂停、恢复和清空的机制
  • 考虑添加超时处理或重试机制

相关概念