回顾异步调用

172 阅读2分钟

不多说,先看代码:

export default class CC {
  constructor(make) {
    this.value = "";
    this.state = "pending";
    this.success = [];
    this.failed = [];
    
    const resolve = (val) => {
      if (this.state !== "pending") return;
      this.state = "success";
      this.value = val;
      this.success.forEach(cb => queueMicrotask(cb)); // ✅ 加入微任务队列
      this.success = [];
    };

    const reject = (val) => {
      if (this.state !== "pending") return;
      this.state = "failed";
      this.value = val;
      this.failed.forEach(cb => queueMicrotask(cb)); // ✅ 加入微任务队列
      this.failed = [];
    };

    try {
      make(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }

  then(isSuccess, isFailed) {
    return new CC((resolve, reject) => { // ✅ 实现链式调用
      const successHandler = () => {
        try {
          resolve(isSuccess?.(this.value)); // ✅ 传递处理结果
        } catch (e) {
          reject(e);
        }
      };

      const failedHandler = () => {
        typeof isFailed === 'function' 
          ? resolve(isFailed(this.value))  // ✅ 捕获错误并继续链式
          : reject(this.value);           // ✅ 未处理错误时继续抛出
      };

      if (this.state === "success") {
        queueMicrotask(successHandler);
      } else if (this.state === "failed") {
        queueMicrotask(failedHandler);
      } else {
        this.success.push(successHandler);
        this.failed.push(failedHandler);
      }
    });
  }
}
// 测试用例 ✅
new CC((_, reject) => {
  setTimeout(() => reject(33333), 3000)
}).then(
  val => console.log('Success:', val),
  err => console.log('Caught Error:', err) // 3秒后输出 ▶ Caught Error: 33333
);

一、构造函数解析

constructor(make) {
  this.value = "";          // 存储终值/拒因
  this.state = "pending";    // 状态机:pending/success/failed
  this.success = [];        // 成功回调队列
  this.failed = [];         // 失败回调队列

  // 核心状态转换函数
  const resolve = (val) => {
    if (this.state !== "pending") return;
    this.state = "success";
    this.value = val;
    this.success.forEach(cb => queueMicrotask(cb)); // ✅ 微任务调度
    this.success = [];      // 清空队列避免重复执行
  };

  const reject = (val) => { /* 类似 resolve 逻辑 */ };

  try {
    make(resolve, reject);  // 同步执行执行器函数
  } catch (err) {
    reject(err);            // ✅ 同步错误直接触发拒绝
  }
}

关键特性:

  • 状态不可逆:通过 state 变量确保只能从 pending 转换到终态
  • 微任务队列:使用 queueMicrotask 实现符合规范的异步调度
  • 错误边界try/catch 包裹执行器函数,同步错误自动触发 reject

二、链式 then 方法实现

then(isSuccess, isFailed) {
  return new CC((resolve, reject) => { // ✅ 返回新实例实现链式
    const successHandler = () => {
      try {
        // ✅ 透传处理结果或捕获异常
        resolve(isSuccess?.(this.value)); 
      } catch (e) {
        reject(e);
      }
    };

    const failedHandler = () => {
      if (typeof isFailed === 'function') {
        resolve(isFailed(this.value)); // ✅ 错误被处理后继续链式
      } else {
        reject(this.value); // ✅ 未处理错误继续向下传递
      }
    };

    // 根据当前状态调度处理程序
    if (this.state === "success") {
      queueMicrotask(successHandler);
    } else if (this.state === "failed") {
      queueMicrotask(failedHandler);
    } else {
      this.success.push(successHandler); // 订阅状态变更
      this.failed.push(failedHandler);
    }
  });
}

核心机制:

  • 返回新 Promise:每个 then 生成独立的新实例,形成调用链

  • 值穿透规则

    • 成功回调 (isSuccess) 不存在时透传终值
    • 失败回调 (isFailed) 不存在时透传拒因
  • 错误冒泡

    • 回调中的同步错误会被捕获并触发新实例的 reject
    • 处理过的错误通过 resolve 转换为成功状态

为什么在then中使用 return 一个新的实例返回一个promise?

是为了能够进行多级的链式调用,比如 new promise(x).then(x).then(x),then本身就是返回一个promise对象