Promise A+ 规范

3 阅读17分钟

Promise A+ 规范

一个为 JS promises 开放的标准。

promise 表示一个异步操作的实际结果。与一个 promise 交互的主要方法是 promise 的 then 方法,then 方法接收两个回调函数,第一个回调会在 promise 兑现时被调用,用于接收 promise 的最终值,第二个回调会在 promise 失败时被调用,用于接收 promise 不能被兑现的原因。

本规范详细描述了 then 方法的行为,提供了一个可操作交互的基础,所有符合 Promise/A+ 规范的 promise 都可以依靠该基础来实现。因此这个规范被认为是非常稳定的,尽管 Promise/A+ 组织可能偶尔地通过一些较小的向后兼容的改变修订规范,来解决新发现的一些边界情况。但是,只有在经过仔细的考虑、讨论和测试后,我们才会集成大的或者向后不兼容的更改。

在历史角度上,Promises/A+ 澄清了早期 Promises/A proposal 中的行为条款,将其扩展到涵盖事实上的行为,并省略了未明确说明或有问题的部分。

本规范没有涉及如何创建、兑现或者拒绝 promise,而是选择专注于 then 方法,未来的工作中,我们可能会提及到这些话题。

术语

  1. "promise" 是一个包含 then 方法且该方法符合规范指定行为的对象或者函数。
  2. "thenable" 是一个定义了 then 方法的对象或者函数。
  3. "value" 是任意合法的 JavaScript 值(包括 undefined、thenable、promise)。
  4. "exception" 是一个由 throw 语句抛出的值。
  5. "reason" 是一个解释了为何 promise 被拒绝的值。

要求

1. Promise 状态

一个 promise 必须是以下三种状态之一:pending、fulfilled、或者 rejected。

  1. 当 promise 状态是 pending 时:
    1. 可以转换为 fulfilled 或者 rejected。
  2. 当 promise 状态是 fulfilled 时:
    1. 不能转换为其他状态。
    2. 必须包含一个不可变的 value。
  3. 当 promise 状态是 rejected 时:
    1. 不能转换为其他状态。
    2. 必须包含一个不可变的 reason。

这里的"不可变"代表的是值的引用相同(即当 === 为真值时)。

2. then 方法

promise 必须提供 then 方法来访问当前的值或最终的原因。

promise 的 then 方法接受 2 个参数:

promise.then(onFulfilled, onRejected)
  1. onFulfilledonRejected 都是可选参数:

    1. 如果 onFulfilled 不是函数则会被忽略。
    2. 如果 onRejected 不是函数则会被忽略。
  2. 如果 onFulfilled 是一个函数:

    1. 它必须在 promise 状态转换为 fulfilled 后调用,promise 的结果作为该函数的第一个参数。
    2. 它不能在 promise 被 fulfilled 前被调用。
    3. 它不能被调用超过一次。
  3. 如果 onRejected 是一个函数:

    1. 它必须在 promise 状态转换为 rejected 后调用,rejected promise 的拒绝原因作为该函数的第一个参数。
    2. 它不能在 promise 被 rejected 前被调用。
    3. 它不能被调用超过一次。
  4. onFulfilledonRejected 不能被调用直到执行上下文栈仅包含平台代码时才能被调用。

  5. onFulfilledonRejected 必须作为函数被调用(即没有 this 值)。

  6. then 对于同一个 promise,可能会被多次调用:

    1. 如果/当 promise 是 fulfilled,所有 onFulfilled 回调函数必须按照最初调用 then 的顺序执行。
    2. 如果/当 promise 是 rejected,所有 onRejected 回调函数必须按照最初调用 then 的顺序执行。
  7. then 方法必须返回一个 promise:

    promise2 = promise1.then(onFulfilled, onRejected)
    
    1. 如果 onFulfilled 或者 onRejected 返回了一个值 x,则执行 [[Resolve]](promise2, x) 解决过程(详见下文)。
    2. 如果 onFulfilledonRejected 抛出一个异常 epromise2 必须使用 e 作为转为失败状态的原因。
    3. 如果 onFulfilled 不是一个函数,并且 promise1 已经兑现,promise2 也必须转为兑现状态,同时使用 promise1 的兑现值作为兑现值。
    4. 如果 onRejected 不是一个函数,并且 promise1 已经转为失败状态,promise2 也必须转为失败状态,同时使用 promise1 的失败原因作为失败原因。

3. Promise 解决过程

Promise 解决过程 是一个抽象的处理输入的 promise 和 x 的操作,表示为 [[Resolve]](promise, x)。如果 x 是一个 thenable,则尝试让 promise 转换为 x 的状态,假设 x 的行为至少有点像 promise。否则将使用 x 作为 promise fulfilled/rejected 的值进行处理。

这种 thenable 的特性使得 Promise 的实现更具有通用性,只要它们暴露了一个符合 Promise/A+ 规范的 then 方法,这同时也使得遵循 Promise/A+ 规范的实现可以与那些不太规范的实现能共存。

要执行 [[Resolve]](promise, x) 操作,需要执行以下步骤:

  1. 如果 promisex 是同一个引用,使用 TypeError 作为理由拒绝 promise
  2. 如果 x 是一个 promise,则采用它的状态:
    1. 如果 x 是 pending,promise 必须保持 pending 直到 x 被 fulfilled 或者被 rejected。
    2. 如果/当 x 是 fulfilled,使用 x 的值兑现 promise
    3. 如果/当 x 是 rejected,使用 x 的值拒绝 promise
  3. 否则,如果 x 是对象或者函数:
    1. 尝试将 x.then 储存至新变量 then(then 保持对 x.then 的引用)。
    2. 如果访问 x.then 时收到异常 e,使用 e 拒绝 promise
    3. 如果 then 是一个函数,将 x 作为 this 调用 then 方法,且第一个参数为 resolvePromise,第二个参数为 rejectPromise,这里:
      1. 如果 resolvePromisey 作为第一个参数被回调,执行 [[Resolve]](promise, y)
      2. 如果/当 rejectPromiser 作为拒绝原因被回调,则用 r 拒绝 promise
      3. 如果 resolvePromiserejectPromise 都被调用,或者同一被多次调用,那么第一次调用优先,以后的调用都会被忽略。
      4. 如果调用中 then 抛出了一个异常 e
        1. 如果 resolvePromise 或者 rejectPromise 已经被调用,则忽略。
        2. 否则,用 e 作为原因,拒绝 promise
    4. 如果 then 不是一个函数,用 x 兑现 promise
  4. 如果 x 不是一个函数或者对象,用 x 兑现 promise

即使 promise 已经在一个循环 thenable 链里被 resolve,由于 [[Resolve]](promise, thenable) 的递归特性,最终将导致 [[Resolve]](promise, thenable) 被再次调用,遵循上面的算法会导致无限递归。规范中并不要求强制处理,但是鼓励实现者检测递归并且用一个具有丰富信息的 TypeError 拒绝 promise

说明

  1. "平台代码"指的是引擎、环境以及 promise 实现代码。实际上,这个要求确保了 onFulfilledonRejected 异步执行,当 then 方法被调用后,事件循环队列有了新的任务。这种特性具体实现可以使用宏任务机制,例如 setTimeoutsetImmediate,或者微任务机制,例如 MutationObserverprocess.nextTick。由于考虑了 promise 在平台的实现,所以它本身可能包含一个任务调度队列来调用回调函数。
  2. 在严格模式下,该函数的 this 将是 undefined;在传统模式下,this 将是全局对象。
  3. 如果实现满足规范所有要求,则可允许 promise2 === promise1,但每一个实现都必须明确指出 promise2 === promise1 会在怎样的条件下出现。
  4. 通常,只有符合本规范的 promise 才是真正的 promise。此条款允许使用实现特定的手段来采用已知合规的 promise 的状态。
  5. 首先存储对 x.then 这一引用,然后测试引用是否有效,然后调用该引用,避免了多次访问 x.then 属性,这种预防措施对于确保访问器属性的一致性非常重要,因为访问器属性的值可能在两次访问中改变。
  6. 实现不应该限制 thenable 链的深度。只有真正的死循环才应该引发一个 TypeError;如果遇到了一个明确的截然不同的无限调用链,那么无限执行下去才是正确的行为。

代码

实现

/**
 * 符合 Promise/A+ 规范的 Promise 实现
 *
 * 核心要点:
 * 1. 状态只能从 pending → fulfilled 或 pending → rejected,且不可逆
 * 2. then 返回一个新的 Promise,支持链式调用
 * 3. 支持值穿透(onFulfilled/onRejected 非函数时透传 value/reason)
 * 4. 回调异步执行(通过微任务 queueMicrotask 调度)
 * 5. 错误捕获(executor 与 then 回调中的异常都会被捕获)
 */

const PENDING = 'pending' as const;
const FULFILLED = 'fulfilled' as const;
const REJECTED = 'rejected' as const;

type PromiseState = typeof PENDING | typeof FULFILLED | typeof REJECTED;

type Executor<T> = (
  resolve: (value: T | PromiseLike<T>) => void,
  reject: (reason?: unknown) => void,
) => void;

type OnFulfilled<T, R> = ((value: T) => R | PromiseLike<R>) | null | undefined;
type OnRejected<R> = ((reason: unknown) => R | PromiseLike<R>) | null | undefined;

/**
 * 解析 then 回调的返回值,处理 thenable 与循环引用
 */
function resolvePromise<T>(
  promise: PromiseAPlus<T>,
  result: unknown,
  resolve: (value: T | PromiseLike<T>) => void,
  reject: (reason?: unknown) => void,
): void {
  // 2.3.1 如果 result 和 promise 相同,抛 TypeError 防止循环引用
  if (result === promise) {
    reject(new TypeError('Chaining cycle detected for promise'));
    return;
  }

  // 2.3.2 如果 result 是 Promise/A+ 实例
  if (result instanceof PromiseAPlus) {
    // 2.3.2.3 如果 result 仍处于 pending,则等待
    if (result.state === PENDING) {
      (result as PromiseAPlus<T>).then(
        (v: T) => resolvePromise(promise, v, resolve, reject),
        (r: unknown) => reject(r),
      );
    } else if (result.state === FULFILLED) {
      resolve(result.value as T);
    } else {
      reject(result.reason);
    }
    return;
  }

  // 2.3.3 如果 result 是 thenable 对象/函数
  if (result !== null && (typeof result === 'object' || typeof result === 'function')) {
    let then: unknown;
    try {
      then = (result as { then?: unknown; }).then;
    } catch (e) {
      // 2.3.3.2 取 then 抛错则直接 reject
      reject(e);
      return;
    }

    // 2.3.3.3 如果 then 是函数,则以 result 为 this 调用
    if (typeof then === 'function') {
      let called = false;
      try {
        (then as (
          res: (v: unknown) => void,
          rej: (r: unknown) => void,
        ) => void).call(
          result,
          (v: unknown) => {
            if (called) return;
            called = true;
            resolvePromise(promise, v, resolve, reject);
          },
          (r: unknown) => {
            if (called) return;
            called = true;
            reject(r);
          },
        );
      } catch (e) {
        // 2.3.3.3.4 如果已调用过 then 的回调,则忽略
        if (called) return;
        called = true;
        reject(e);
      }
      return;
    }
  }

  // 2.3.4 普通值,直接 resolve
  resolve(result as T | PromiseLike<T>);
}

export class PromiseAPlus<T> implements PromiseLike<T> {
  private _state: PromiseState = PENDING;
  private _value!: T;
  private _reason!: unknown;

  // 回调队列:pending 期间收集,状态确定后异步执行
  private readonly _onFulfilledCallbacks: Array<() => void> = [];
  private readonly _onRejectedCallbacks: Array<() => void> = [];

  constructor(executor: Executor<T>) {
    const resolve = (value: T | PromiseLike<T>): void => {
      if (this._state !== PENDING) return;
      // 2.3 统一通过 resolvePromise 处理,支持 PromiseAPlus 与通用 thenable
      resolvePromise(
        this as unknown as PromiseAPlus<unknown>,
        value,
        (v: unknown) => this._doResolve(v as T),
        (r: unknown) => this._doReject(r),
      );
    };

    const reject = (reason?: unknown): void => {
      if (this._state !== PENDING) return;
      this._doReject(reason);
    };

    try {
      executor(resolve, reject);
    } catch (e) {
      // executor 抛异常直接 reject(前提是尚未确定状态)
      reject(e);
    }
  }

  private _doResolve(value: T): void {
    if (this._state !== PENDING) return;
    this._state = FULFILLED;
    this._value = value;
    // 2.2.4 异步执行回调
    for (const cb of this._onFulfilledCallbacks) {
      queueMicrotask(cb);
    }
    this._onFulfilledCallbacks.length = 0;
    this._onRejectedCallbacks.length = 0;
  }

  private _doReject(reason: unknown): void {
    if (this._state !== PENDING) return;
    this._state = REJECTED;
    this._reason = reason;
    for (const cb of this._onRejectedCallbacks) {
      queueMicrotask(cb);
    }
    this._onFulfilledCallbacks.length = 0;
    this._onRejectedCallbacks.length = 0;
  }

  get state(): PromiseState {
    return this._state;
  }

  get value(): T {
    return this._value;
  }

  get reason(): unknown {
    return this._reason;
  }

  then<TResult1 = T, TResult2 = never>(
    onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null | undefined,
    onRejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null | undefined,
  ): PromiseAPlus<TResult1 | TResult2> {
    const promise2 = new PromiseAPlus<TResult1 | TResult2>((resolve, reject) => {
      const handleFulfilled = (): void => {
        try {
          if (typeof onFulfilled !== 'function') {
            // 2.2.7.3 值穿透
            resolve(this._value as unknown as TResult1 | TResult2 | PromiseLike<TResult1 | TResult2>);
          } else {
            const x = onFulfilled(this._value);
            resolvePromise(promise2, x, resolve, reject);
          }
        } catch (e) {
          reject(e);
        }
      };

      const handleRejected = (): void => {
        try {
          if (typeof onRejected !== 'function') {
            // 2.2.7.4 错误透传
            reject(this._reason);
          } else {
            const x = onRejected(this._reason);
            resolvePromise(promise2, x, resolve, reject);
          }
        } catch (e) {
          reject(e);
        }
      };

      if (this._state === FULFILLED) {
        // 2.2.4 已确定状态,异步执行
        queueMicrotask(handleFulfilled);
      } else if (this._state === REJECTED) {
        queueMicrotask(handleRejected);
      } else {
        // pending:先入队,待 resolve/reject 时触发
        this._onFulfilledCallbacks.push(handleFulfilled);
        this._onRejectedCallbacks.push(handleRejected);
      }
    });

    return promise2;
  }

  catch<TResult = T>(onRejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null | undefined): PromiseAPlus<T | TResult> {
    return this.then(undefined, onRejected);
  }

  finally(onFinally?: () => void): PromiseAPlus<T> {
    return this.then<T, never>(
      (value: T): T | PromiseLike<T> =>
        PromiseAPlus.resolve(
          typeof onFinally === 'function' ? onFinally() : undefined,
        ).then(() => value) as unknown as T | PromiseLike<T>,
      (reason: unknown): never | PromiseLike<never> =>
        PromiseAPlus.resolve(
          typeof onFinally === 'function' ? onFinally() : undefined,
        ).then(() => {
          throw reason;
        }) as unknown as never | PromiseLike<never>,
    ) as PromiseAPlus<T>;
  }

  static resolve(): PromiseAPlus<void>;
  static resolve<U>(value: U | PromiseLike<U>): PromiseAPlus<U>;
  static resolve<U>(value?: U | PromiseLike<U>): PromiseAPlus<U> {
    if (value instanceof PromiseAPlus) {
      return value as PromiseAPlus<U>;
    }
    return new PromiseAPlus<U>((resolve) => resolve(value as U));
  }

  static reject<U = never>(reason?: unknown): PromiseAPlus<U> {
    return new PromiseAPlus<U>((_, reject) => reject(reason));
  }

  static all<U>(promises: Iterable<U | PromiseLike<U>>): PromiseAPlus<U[]> {
    return new PromiseAPlus<U[]>((resolve, reject) => {
      const arr = Array.from(promises);
      if (arr.length === 0) {
        resolve([]);
        return;
      }
      const result: U[] = new Array(arr.length);
      let remaining = arr.length;
      arr.forEach((p, index) => {
        PromiseAPlus.resolve(p).then(
          (v: U) => {
            result[index] = v;
            if (--remaining === 0) resolve(result);
          },
          (r: unknown) => reject(r),
        );
      });
    });
  }

  static race<U>(promises: Iterable<U | PromiseLike<U>>): PromiseAPlus<U> {
    return new PromiseAPlus<U>((resolve, reject) => {
      const arr = Array.from(promises);
      arr.forEach((p) => {
        PromiseAPlus.resolve(p).then(
          (v: U) => resolve(v),
          (r: unknown) => reject(r),
        );
      });
    });
  }

  static allSettled<U>(
    promises: Iterable<U | PromiseLike<U>>,
  ): PromiseAPlus<Array<{ status: 'fulfilled'; value: U; } | { status: 'rejected'; reason: unknown; }>> {
    return new PromiseAPlus((resolve) => {
      const arr = Array.from(promises);
      if (arr.length === 0) {
        resolve([]);
        return;
      }
      const result: Array<{ status: 'fulfilled'; value: U; } | { status: 'rejected'; reason: unknown; }> = new Array(arr.length);
      let remaining = arr.length;
      arr.forEach((p, index) => {
        PromiseAPlus.resolve(p).then(
          (v: U) => {
            result[index] = { status: 'fulfilled', value: v };
            if (--remaining === 0) resolve(result);
          },
          (r: unknown) => {
            result[index] = { status: 'rejected', reason: r };
            if (--remaining === 0) resolve(result);
          },
        );
      });
    });
  }

  // Promise/A+ 测试适配器所需
  static deferred<U>() {
    let resolve!: (value: U | PromiseLike<U>) => void;
    let reject!: (reason?: unknown) => void;
    const promise = new PromiseAPlus<U>((res, rej) => {
      resolve = res;
      reject = rej;
    });
    return { promise, resolve, reject };
  }
}

测试

import { describe, it, expect, vi } from 'vitest'
import { PromiseAPlus } from './promise-aplus'

describe('PromiseAPlus — Promise/A+ 规范', () => {
  // ============================================================
  // 2.1 Promise 状态
  // ============================================================
  describe('2.1 Promise 状态', () => {
    describe('2.1.1 pending', () => {
      it('2.1.1.1 pending 可以转换为 fulfilled', () => {
        const p = new PromiseAPlus<number>((resolve) => resolve(1))
        return p.then((v) => {
          expect(v).toBe(1)
          expect(p.state).toBe('fulfilled')
        })
      })

      it('2.1.1.1 pending 可以转换为 rejected', () => {
        const p = new PromiseAPlus<number>((_, reject) => reject(new Error('err')))
        return p.then(
          () => expect.fail('should not be fulfilled'),
          (e) => {
            expect((e as Error).message).toBe('err')
            expect(p.state).toBe('rejected')
          },
        )
      })
    })

    describe('2.1.2 fulfilled', () => {
      it('2.1.2.1 fulfilled 后不能再转换为其他状态', () => {
        const p = new PromiseAPlus<number>((resolve, reject) => {
          resolve(1)
          reject(new Error('should be ignored'))
          resolve(2)
        })
        return p.then(
          (v) => expect(v).toBe(1),
          () => expect.fail('should not be rejected'),
        )
      })

      it('2.1.2.2 fulfilled 后必须有一个不可变的 value', () => {
        const obj = { a: 1 }
        const p = new PromiseAPlus<typeof obj>((resolve) => resolve(obj))
        return p.then((v) => {
          expect(v).toBe(obj) // 引用相同
        })
      })
    })

    describe('2.1.3 rejected', () => {
      it('2.1.3.1 rejected 后不能再转换为其他状态', () => {
        const p = new PromiseAPlus<number>((resolve, reject) => {
          reject(new Error('first'))
          resolve(1)
          reject(new Error('second'))
        })
        return p.then(
          () => expect.fail('should not be fulfilled'),
          (e) => expect((e as Error).message).toBe('first'),
        )
      })

      it('2.1.3.2 rejected 后必须有一个不可变的 reason', () => {
        const err = new Error('reason')
        const p = new PromiseAPlus<number>((_, reject) => reject(err))
        return p.then(
          () => expect.fail('should not be fulfilled'),
          (e) => expect(e).toBe(err), // 引用相同
        )
      })
    })
  })

  // ============================================================
  // 2.2 then 方法
  // ============================================================
  describe('2.2 then 方法', () => {
    describe('2.2.1 onFulfilled 和 onRejected 是可选参数', () => {
      it('2.2.1.1 onFulfilled 不是函数时被忽略(值穿透)', () => {
        return PromiseAPlus.resolve(1)
          .then(null as never)
          .then('not a function' as unknown as never)
          .then((v) => expect(v).toBe(1))
      })

      it('2.2.1.2 onRejected 不是函数时被忽略(错误透传)', () => {
        return PromiseAPlus.reject(new Error('pass'))
          .then(undefined, null as never)
          .then(undefined, 'not a function' as unknown as never)
          .then(
            () => expect.fail('should not be fulfilled'),
            (e) => expect((e as Error).message).toBe('pass'),
          )
      })
    })

    describe('2.2.2 onFulfilled', () => {
      it('2.2.2.1 在 promise fulfilled 后以 value 为第一个参数调用', () => {
        return PromiseAPlus.resolve(42).then((v) => expect(v).toBe(42))
      })

      it('2.2.2.2 不能在 promise fulfilled 前被调用', () => {
        let called = false
        const p = new PromiseAPlus<number>((resolve) => {
          setTimeout(() => resolve(1), 10)
        })
        p.then(() => {
          called = true
        })
        return Promise.resolve().then(() => {
          expect(called).toBe(false) // 此时 promise 尚未 resolve
        })
      })

      it('2.2.2.3 不能被调用超过一次', () => {
        const spy = vi.fn()
        const p = new PromiseAPlus<number>((resolve) => resolve(1))
        p.then(spy)
        return Promise.resolve().then(() => {
          expect(spy).toHaveBeenCalledTimes(1)
        })
      })
    })

    describe('2.2.3 onRejected', () => {
      it('2.2.3.1 在 promise rejected 后以 reason 为第一个参数调用', () => {
        return PromiseAPlus.reject(new Error('bad')).then(
          () => expect.fail('should not be fulfilled'),
          (e) => expect((e as Error).message).toBe('bad'),
        )
      })

      it('2.2.3.2 不能在 promise rejected 前被调用', () => {
        let called = false
        const p = new PromiseAPlus<number>((_, reject) => {
          setTimeout(() => reject(new Error('err')), 10)
        })
        p.then(undefined, () => {
          called = true
        })
        return Promise.resolve().then(() => {
          expect(called).toBe(false)
        })
      })

      it('2.2.3.3 不能被调用超过一次', () => {
        const spy = vi.fn()
        const p = new PromiseAPlus<number>((_, reject) => reject(new Error('err')))
        p.then(undefined, spy)
        return Promise.resolve().then(() => {
          expect(spy).toHaveBeenCalledTimes(1)
        })
      })
    })

    describe('2.2.4 onFulfilled/onRejected 异步执行', () => {
      it('2.2.4 不能在执行上下文栈仅包含平台代码前被调用', () => {
        const order: string[] = []
        const p = PromiseAPlus.resolve(1)
        p.then(() => order.push('then'))
        order.push('sync')
        return Promise.resolve().then(() => {
          expect(order).toEqual(['sync', 'then'])
        })
      })

      it('2.2.4 rejected 回调也异步执行', () => {
        const order: string[] = []
        const p = PromiseAPlus.reject(new Error('err'))
        p.then(undefined, () => order.push('catch'))
        order.push('sync')
        return Promise.resolve().then(() => {
          expect(order).toEqual(['sync', 'catch'])
        })
      })
    })

    describe('2.2.5 onFulfilled/onRejected 作为函数调用(无 this)', () => {
      it('2.2.5 在严格模式下 this 为 undefined', () => {
        let capturedThis: unknown = 'initial'
        return PromiseAPlus.resolve(1)
          .then(function (this: unknown) {
            capturedThis = this
          })
          .then(() => expect(capturedThis).toBeUndefined())
      })
    })

    describe('2.2.6 then 可被多次调用', () => {
      it('2.2.6.1 多个 onFulfilled 按注册顺序执行', () => {
        const order: string[] = []
        const p = PromiseAPlus.resolve(1)
        p.then(() => order.push('a'))
        p.then(() => order.push('b'))
        p.then(() => order.push('c'))
        return Promise.resolve().then(() => {
          expect(order).toEqual(['a', 'b', 'c'])
        })
      })

      it('2.2.6.2 多个 onRejected 按注册顺序执行', () => {
        const order: string[] = []
        const p = PromiseAPlus.reject(new Error('err'))
        p.then(undefined, () => order.push('a'))
        p.then(undefined, () => order.push('b'))
        p.then(undefined, () => order.push('c'))
        return Promise.resolve().then(() => {
          expect(order).toEqual(['a', 'b', 'c'])
        })
      })

      it('2.2.6 pending 状态下多个回调按注册顺序执行', () => {
        const order: number[] = []
        const p = new PromiseAPlus<number>((resolve) => {
          setTimeout(() => resolve(1), 10)
        })
        p.then(() => order.push(1))
        p.then(() => order.push(2))
        p.then(() => order.push(3))
        return p.then(() => {
          // 等待所有微任务执行完
          return Promise.resolve().then(() => {
            expect(order).toEqual([1, 2, 3])
          })
        })
      })
    })

    describe('2.2.7 then 必须返回一个 promise', () => {
      it('2.2.7 then 返回的值是 PromiseAPlus 实例且不等于原 promise', () => {
        const p1 = PromiseAPlus.resolve(1)
        const p2 = p1.then(() => 2)
        expect(p2).not.toBe(p1)
        expect(p2).toBeInstanceOf(PromiseAPlus)
      })

      describe('2.2.7.1 onFulfilled/onRejected 返回值 x 执行 [[Resolve]]', () => {
        it('返回普通值', () => {
          return PromiseAPlus.resolve(1)
            .then(() => 2)
            .then((v) => expect(v).toBe(2))
        })

        it('返回 null', () => {
          return PromiseAPlus.resolve(1)
            .then(() => null)
            .then((v) => expect(v).toBeNull())
        })

        it('返回 undefined', () => {
          return PromiseAPlus.resolve(1)
            .then(() => undefined)
            .then((v) => expect(v).toBeUndefined())
        })

        it('返回对象', () => {
          const obj = { a: 1 }
          return PromiseAPlus.resolve(1)
            .then(() => obj)
            .then((v) => expect(v).toBe(obj))
        })

        it('返回另一个 PromiseAPlus(fulfilled)', () => {
          return PromiseAPlus.resolve(1)
            .then(() => PromiseAPlus.resolve(3))
            .then((v) => expect(v).toBe(3))
        })

        it('返回另一个 PromiseAPlus(rejected)', () => {
          return PromiseAPlus.resolve(1)
            .then(() => PromiseAPlus.reject(new Error('nested')))
            .then(
              () => expect.fail('should not be fulfilled'),
              (e) => expect((e as Error).message).toBe('nested'),
            )
        })

        it('返回 thenable 对象', () => {
          return PromiseAPlus.resolve(1)
            .then((v) => ({
              then: (resolve?: ((n: number) => void) | null) => resolve!(v + 10),
            } as PromiseLike<number>))
            .then((v) => expect(v).toBe(11))
        })

        it('onRejected 返回值也会被 resolve', () => {
          return PromiseAPlus.reject(new Error('err'))
            .then(undefined, () => 'recovered')
            .then((v) => expect(v).toBe('recovered'))
        })
      })

      describe('2.2.7.2 onFulfilled/onRejected 抛异常 e,promise2 reject(e)', () => {
        it('onFulfilled 抛异常', () => {
          return PromiseAPlus.resolve(1)
            .then(() => {
              throw new Error('throw')
            })
            .then(
              () => expect.fail('should not be fulfilled'),
              (e) => expect((e as Error).message).toBe('throw'),
            )
        })

        it('onRejected 抛异常', () => {
          return PromiseAPlus.reject(new Error('first'))
            .then(undefined, () => {
              throw new Error('second')
            })
            .then(
              () => expect.fail('should not be fulfilled'),
              (e) => expect((e as Error).message).toBe('second'),
            )
        })
      })

      describe('2.2.7.3 onFulfilled 非函数时 promise2 透传 value', () => {
        it('值穿透 fulfilled', () => {
          return PromiseAPlus.resolve(42)
            .then(null as never)
            .then((v) => expect(v).toBe(42))
        })

        it('值穿透多层', () => {
          return PromiseAPlus.resolve(42)
            .then(null)
            .then(undefined)
            .then('x' as unknown as never)
            .then((v) => expect(v).toBe(42))
        })
      })

      describe('2.2.7.4 onRejected 非函数时 promise2 透传 reason', () => {
        it('错误透传 rejected', () => {
          return PromiseAPlus.reject(new Error('pass'))
            .then(undefined, null as never)
            .then(
              () => expect.fail('should not be fulfilled'),
              (e) => expect((e as Error).message).toBe('pass'),
            )
        })

        it('错误透传多层', () => {
          return PromiseAPlus.reject(new Error('pass'))
            .then(undefined, undefined)
            .then(undefined, null)
            .then(
              () => expect.fail('should not be fulfilled'),
              (e) => expect((e as Error).message).toBe('pass'),
            )
        })
      })
    })
  })

  // ============================================================
  // 2.3 Promise 解决过程 [[Resolve]](promise, x)
  // ============================================================
  describe('2.3 Promise 解决过程', () => {
    describe('2.3.1 promise === x,TypeError 拒绝', () => {
      it('返回自身 promise 导致循环引用', () => {
        const p = PromiseAPlus.resolve(1)
        const p2: PromiseAPlus<number> = p.then(() => p2)
        return p2.then(
          () => expect.fail('should not be fulfilled'),
          (e) => expect(e).toBeInstanceOf(TypeError),
        )
      })
    })

    describe('2.3.2 x 是 promise,采用其状态', () => {
      it('2.3.2.1 x 为 pending,promise 保持 pending', async () => {
        const inner = new PromiseAPlus<number>((resolve) => {
          setTimeout(() => resolve(1), 10)
        })
        const p2 = PromiseAPlus.resolve(1).then(() => inner)
        // 立即检查应为 pending
        await Promise.resolve()
        expect(p2.state).toBe('pending')
        return p2.then((v) => expect(v).toBe(1))
      })

      it('2.3.2.2 x 为 fulfilled,promise 以 x 的 value fulfilled', () => {
        return PromiseAPlus.resolve(1)
          .then(() => PromiseAPlus.resolve(99))
          .then((v) => expect(v).toBe(99))
      })

      it('2.3.2.3 x 为 rejected,promise 以 x 的 reason rejected', () => {
        return PromiseAPlus.resolve(1)
          .then(() => PromiseAPlus.reject(new Error('rej')))
          .then(
            () => expect.fail('should not be fulfilled'),
            (e) => expect((e as Error).message).toBe('rej'),
          )
      })
    })

    describe('2.3.3 x 是对象或函数', () => {
      it('2.3.3.2 读取 x.then 抛异常,以该异常 reject', () => {
        const obj = {}
        Object.defineProperty(obj, 'then', {
          get() {
            throw new Error('get then error')
          },
        })
        return PromiseAPlus.resolve(1)
          .then(() => obj as PromiseLike<unknown>)
          .then(
            () => expect.fail('should not be fulfilled'),
            (e) => expect((e as Error).message).toBe('get then error'),
          )
      })

      describe('2.3.3.3 then 是函数', () => {
        it('2.3.3.3.1 resolvePromise(y) 递归解决', () => {
          return PromiseAPlus.resolve(1)
            .then(() => ({
              then: (resolve: (v: unknown) => void) =>
                resolve({ then: (r: (v: unknown) => void) => r(42) }),
            } as PromiseLike<unknown>))
            .then((v) => expect(v).toBe(42))
        })

        it('2.3.3.3.2 rejectPromise(r) 直接拒绝', () => {
          return PromiseAPlus.resolve(1)
            .then(() => ({
              then: (_resolve: unknown, reject: (r: unknown) => void) =>
                reject(new Error('thenable reject')),
            } as PromiseLike<unknown>))
            .then(
              () => expect.fail('should not be fulfilled'),
              (e) => expect((e as Error).message).toBe('thenable reject'),
            )
        })

        it('2.3.3.3.3 resolvePromise 和 rejectPromise 都调用,首次优先', () => {
          return PromiseAPlus.resolve(1)
            .then(() => ({
              then: (
                resolve: (v: unknown) => void,
                reject: (r: unknown) => void,
              ) => {
                resolve(1)
                reject(new Error('should be ignored'))
                resolve(2)
              },
            } as PromiseLike<unknown>))
            .then((v) => expect(v).toBe(1))
        })

        it('2.3.3.3.3 reject 先调用后 resolve 被忽略', () => {
          return PromiseAPlus.resolve(1)
            .then(() => ({
              then: (
                resolve: (v: unknown) => void,
                reject: (r: unknown) => void,
              ) => {
                reject(new Error('first'))
                resolve(2)
                reject(new Error('second'))
              },
            } as PromiseLike<unknown>))
            .then(
              () => expect.fail('should not be fulfilled'),
              (e) => expect((e as Error).message).toBe('first'),
            )
        })

        it('2.3.3.3.4 then 抛异常但 resolvePromise 已调用则忽略', () => {
          return PromiseAPlus.resolve(1)
            .then(() => ({
              then: (resolve: (v: unknown) => void) => {
                resolve(42)
                throw new Error('should be ignored')
              },
            } as PromiseLike<unknown>))
            .then((v) => expect(v).toBe(42))
        })

        it('2.3.3.3.4 then 抛异常且未调用回调,以异常 reject', () => {
          return PromiseAPlus.resolve(1)
            .then(() => ({
              then: () => {
                throw new Error('then throws')
              },
            } as PromiseLike<unknown>))
            .then(
              () => expect.fail('should not be fulfilled'),
              (e) => expect((e as Error).message).toBe('then throws'),
            )
        })

        it('2.3.3.3 then 以 x 为 this 调用', () => {
          const thenable = {
            label: 'I am x',
            then(this: { label: string }, resolve: (v: unknown) => void) {
              resolve(this.label)
            },
          }
          return PromiseAPlus.resolve(1)
            .then(() => thenable as unknown as PromiseLike<unknown>)
            .then((v) => expect(v).toBe('I am x'))
        })
      })

      it('2.3.3.4 then 不是函数,以 x 兑现', () => {
        return PromiseAPlus.resolve(1)
          .then(() => ({ then: 'not a function', a: 1 }))
          .then((v) => expect(v).toEqual({ then: 'not a function', a: 1 }))
      })

      it('2.3.3 x 是函数(非 thenable)以 x 兑现', () => {
        const fn = () => 42
        return PromiseAPlus.resolve(1)
          .then(() => fn)
          .then((v) => expect(v).toBe(fn))
      })
    })

    describe('2.3.4 x 不是对象或函数,以 x 兑现', () => {
      it('x 是数字', () => {
        return PromiseAPlus.resolve(1)
          .then(() => 123)
          .then((v) => expect(v).toBe(123))
      })

      it('x 是字符串', () => {
        return PromiseAPlus.resolve(1)
          .then(() => 'hello')
          .then((v) => expect(v).toBe('hello'))
      })

      it('x 是 boolean', () => {
        return PromiseAPlus.resolve(1)
          .then(() => true)
          .then((v) => expect(v).toBe(true))
      })

      it('x 是 null', () => {
        return PromiseAPlus.resolve(1)
          .then(() => null)
          .then((v) => expect(v).toBeNull())
      })

      it('x 是 undefined', () => {
        return PromiseAPlus.resolve(1)
          .then(() => undefined)
          .then((v) => expect(v).toBeUndefined())
      })
    })
  })

  // ============================================================
  // 补充:executor 错误捕获 & 静态方法
  // ============================================================
  describe('executor 错误捕获', () => {
    it('executor 抛出同步异常被捕获为 rejected', () => {
      return new PromiseAPlus<number>(() => {
        throw new Error('boom')
      }).then(
        () => expect.fail('should not be fulfilled'),
        (e) => expect((e as Error).message).toBe('boom'),
      )
    })

    it('executor resolve 后抛异常被忽略', () => {
      return new PromiseAPlus<number>((resolve) => {
        resolve(1)
        throw new Error('ignored')
      }).then((v) => expect(v).toBe(1))
    })
  })

  describe('静态方法', () => {
    it('resolve 无参数返回 undefined', () => {
      return PromiseAPlus.resolve().then((v) => expect(v).toBeUndefined())
    })

    it('resolve 传入 thenable 会被展开', () => {
      return PromiseAPlus.resolve({
        then: (resolve: (v: number) => void) => resolve(42),
      } as PromiseLike<number>).then((v) => expect(v).toBe(42))
    })

    it('all 全部成功', () => {
      return PromiseAPlus.all([PromiseAPlus.resolve(1), PromiseAPlus.resolve(2), 3]).then(
        (arr) => expect(arr).toEqual([1, 2, 3]),
      )
    })

    it('all 有失败则整体失败', () => {
      return PromiseAPlus.all([
        PromiseAPlus.resolve(1),
        PromiseAPlus.reject(new Error('fail')),
      ]).then(
        () => expect.fail('should not be fulfilled'),
        (e) => expect((e as Error).message).toBe('fail'),
      )
    })

    it('all 空数组立即 resolve', () => {
      return PromiseAPlus.all([]).then((arr) => expect(arr).toEqual([]))
    })

    it('race 返回第一个完成的结果', () => {
      return PromiseAPlus.race([
        new PromiseAPlus<number>((resolve) => setTimeout(() => resolve(2), 20)),
        PromiseAPlus.resolve(1),
      ]).then((v) => expect(v).toBe(1))
    })

    it('allSettled 等待全部完成', () => {
      return PromiseAPlus.allSettled([
        PromiseAPlus.resolve(1),
        PromiseAPlus.reject(new Error('x')),
      ]).then((results) => {
        expect(results).toEqual([
          { status: 'fulfilled', value: 1 },
          { status: 'rejected', reason: new Error('x') },
        ])
      })
    })
  })

  describe('finally', () => {
    it('fulfilled 时执行 finally 并透传值', () => {
      let called = false
      return PromiseAPlus.resolve(1)
        .finally(() => {
          called = true
        })
        .then((v) => {
          expect(called).toBe(true)
          expect(v).toBe(1)
        })
    })

    it('rejected 时执行 finally 并透传错误', () => {
      let called = false
      return PromiseAPlus.reject(new Error('err'))
        .finally(() => {
          called = true
        })
        .then(
          () => expect.fail('should not be fulfilled'),
          (e) => {
            expect(called).toBe(true)
            expect((e as Error).message).toBe('err')
          },
        )
    })
  })
})