promiseA+规范
一个开放、健全且通用的JavaScript Promise标准 --- 由实现者指定,为实现者服务
一个promise代表了某个异步操作的最终结果。与promise交互的主要方式是通过其 then 方法,该方法注册了两个回调函数,一个用于接收promise的最终值(fulfillment value),另一个则是用于接收promise不能被履行的原因(rejection reason)。
本规范详细规定了 then 方法的行为,为所有符合PromiseA+规范的实施提供了一个可互操作的基础。因此,本规范应被视为非常稳定。尽管PromiseA+组织可能会为了处理新发现的极端情况而偶尔修订本规范,但只会经过仔细权衡、讨论和测试后,才会进行不兼容的较大更改或向下兼容的修订。
从历史上看,PromiseA+澄清了早期PromiseA提案中的行为条款,将其在事实上的扩展约定明确化,并省略了那些未经验证或有问题的部分。
最后,PromiseA+规范的核心内容没有涉及如何创建(create)、履行(fulfill)或拒绝(reject)promise,而是选择专注于一个可互操作的 then 方法。未来在配套规范中可能涉及这些主题。
1.术语
1.1 promise是一个拥有 then 方法的对象或函数,其行为符合本规范。
1.2 thenable是一个定义了 then 方法的对象或函数。
1.3 值(value)是任何合法的JavaScript值(包括undefined/thenable/promise)。
1.4 异常(exception)是使用 throw 语句抛出的值。
1.5 拒绝原因(reason)是一个值,用于说明promise被拒绝的原因。
2. 要求
2.1 Promise状态
一个promise必须处于以下三种状态之一:等待中(pending)、已履行(fulfilled)或已拒绝(rejected)。
2.1.1 当处于等待中(pending)状态时,promise:
2.1.1.1 可以转换到已履行(fulfilled)或者已拒绝(rejected)状态。
2.1.2 当处于已履行(fulfilled)状态时,promise:
2.1.2.1 不能转换任何其他状态。
2.1.2.2 必须是一个值,且该值不能改变。
2.1.3 当处于已拒绝(rejected)状态时,promise:
2.1.3.1 不能转换任何其他状态。
2.1.3.2 必须有一个拒绝原因,且原因不能更改。
这里的“不能更改”指的是不可变身份(即===),并不意味着深度不可变。
2.2 then方法
promise必须提供一个then方法来访问当前或最终值或拒绝原因。
一个promise的 then 方法接收两个参数:
promise.then(onFulfilled,onRejected)
2.2.1 onFulfilled和 onRejected 都是可选参数:
2.2.1.1 如果 onFulfilled 不是函数,它必须被忽略。
2.2.1.2 如果 onRejected 不是函数,它必须被忽略。
2.2.2 如果 onFulfilled 是一个函数:
2.2.2.1 它必须在 promise 进入已履行状态后被调用,并将 promise 的值作为其第一个参数。
2.2.2.2 它在 promise 进入已履行状态之前不能被调用。 2.2.2.3 它最多只能被调用一次。
2.2.3 如果 onRejected 是一个函数:
2.2.3.1 它必须在 promise 进入 已拒绝 状态后被调用,并将 promise 的 拒绝原因 作为其第一个参数。
2.2.3.2 它在 promise 进入已拒绝状态之前不能被调用。
2.2.3.3 它最多只能被调用一次。
2.2.4 onFulfilled和 onRejected 必须必须等到执行上下文栈中仅包含平台代码时才能背调用。[3.1]
2.2.5 onFulfilled和 onRejected 必须作为函数调用(即没有 this 值)。 [3.2]
2.2.6 同一个 then 方法可以在用一个promise上被多次调用。
2.2.6.1 当 promise 进入 已履行状态时,所有相应的 onFulfilled 回调必须按照它们原始调用 then 的顺序执行。
2.2.6.2 当 promise 进入 已拒绝状态时,所有相应的 onRejected 回调必须按照它们原始调用 then 的顺序执行。
2.2.7 then 方法必须返回一个promise。[3.3]
promise2 = promise1.then(onFulfilled,onRejected)
2.2.7.1 如果 onFulfilled 或 onRejected 返回一个值 x ,则执行promise解决过程 [[Resolve]](promise2,x) 。
2.2.7.2 如果 onFulfilled或onRejected 抛出一个异常e ,则promise2 必须以e作为拒绝原因被拒绝。
2.2.7.3 如果 onFulfilled 不是一个函数,并且promise1 进入已履行状态,那么promise2 必须以与promise1 相同的值进入已履行状态。
2.2.7.4 如果onRejected 不是一个函数,并且promise1 进入已拒绝状态,那么promise2 必须以与promise1 相同的 拒绝原因进入 已拒绝状态。
2.3 Promise解决过程
Promise解决过程 是一个抽象操作,它以一个promise和一个值作为输入,我们将其表示为 [[Resolve]](promise,x) 。如果 x 是一个thenable,它会尝试让 promise 采用 x 的状态(前提是x 的行为至少像一个promise)。否则,它将用值 x 来履行 promise。
这种对thenable的处理使得promise可以实现互操作,只要它们公开一个符合PromiseA+规范的then 方法。它还允许PromiseA+实现通过合理的then 方法来”同化“(assimilate)不符合规范的实现。
要运行 [[Resolve]](promise,x) ,请执行一下步骤:
2.3.1 如果 promise 和 x 指向同一个对象,则以一个 TypeError 作为 拒绝原因来拒绝 promise 。
2.3.2 如果 x 是一个promise,则采用其状态:[3.4]
2.3.2.1 如果 x 处于等待中(pending)状态, promise 必须保持等待中状态,直到 x 被履行或被拒绝。
2.3.2.2 如果 x 处于已履行(fulfilled)状态,用相同的值履行 promise 。
2.3.2.3 如果 x 处于已拒绝(rejected)状态,用相同的错误原因拒绝 promise 。
2.3.3 否则,如果 x 是一个对象或函数。则:
2.3.3.1 令 then 为 x.then 。[3.5]
2.3.3.2 如果获取 x.then 属性的结果抛出一个异常 e ,则以 e 作为拒绝原因拒绝 promise 。
2.3.3.3 如果 then 是一个函数,则将 x 作为 this 来调用它,第一个参数是 resolvePromise ,第二个参数是 rejectPromise ,其中:
2.3.3.3.1 如果 resolvePromise 被一个 y 调用,则执行 [[Resolve]](promise,y)。
2.3.3.3.2 如果 resolvePromise 被一个拒绝原因 r 调用,则以 r 拒绝 promise 。
2.3.3.3.3 如果 resolvePromise 和 rejectPromise 都被调用,或者对同一个参数进行多次调用,则首次调用优先,任何后续的调用都将被忽略。
2.3.3.3.4 如果调用 then 方法抛出了异常 e :
2.3.3.3.4.1 如果 resolvePromise 或 rejectPromise 已经被调用过,则忽略该异常。
2.3.3.3.4.2 否则,以 e 作为拒绝原因拒绝 promise 。
2.3.3.4 如果 then 不是一个函数,则以 x 作为值履行 promise 。
2.3.4 如果 x 不是对象或函数,则以 x 作为值履行 promise 。
如果一个promise被一个thenable解决,而这个thenable参与了一个循环的thenable链,这样 [[Resolve]](promise,thenable) 的递归性质最终会导致 [[Resolve]](promise,thenable) 再次被调用,按照上述算法,这将导致无线递归。我们鼓励但是不强制要求实现检测这种递归并以一个信息丰富的 TypeError 作为拒绝原因来拒绝 promise 。[3.6]
3. 注释
- 这里的“平台代码”指的是引擎、环境以及Promise实现代码。在实践中,这个要求确保了
onFulfilled和onRejected能够异步的、在新的一轮事件循环中(after the event loop turn in whichthenis called)执行,并且调用栈是最新的。这可以通过“macro-task” 机制(如setTimeout或setImmediate)或“micro-task”(如MutationObserver 或 process.nextTick)来实现。由于promise实现本身被认为是“平台代码”,因此处理程序被调用时,它本身可能已经包含一个任务调度队列或“trampoline”。 - 在严格模式下(strict mode),
this的值是undefined;在非严格模式下(sloppy mode),this的值是全局对象。 - 如果实现满足所有要求,则允许
promise2 === promise1。每个实现都应该记录它是否能产生promise2===promise1以及在什么条件下会产生。 - 通常,只有当
x来自当前实现时,我们才知道它是一个真正的promise。这条条款允许使用特定实现的方法来采用一直符合规范的promise的状态。 - 此过程首先存储对
x.then的引用,然后测试该引用,然后在调用该引用,以避免多次访问x.then属性。这种预防措施对于确保在属性访问过程中行为的一致性非常重要,因为属性的值在多次检索之间可能会发生变化。 - 实现不应该在thenable链的额深度上做任何限制,并且假设超出任意限制的递归是无限的。只有真正的循环才会引起
TypeError;如果遇到一个无线长的不同thenable链,那么无线递归下去时正确的行为。
官网原文参见
完整Promise手写源码(带上所有ES6+静态方法)
/**
* 符合 Promise A+ 规范的 Promise 实现
* 参考:https://promisesaplus.com/
*/
// Promise 的三种状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
/**
* Promise A+ 2.3: The Promise Resolution Procedure
* 处理 Promise 解析过程
*/
function resolvePromise(promise, x, resolve, reject) {
// 2.3.1: 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
if (promise === x) {
return reject(new TypeError('Chaining cycle detected for promise'));
}
// 2.3.2: 如果 x 为 Promise,则使 promise 接受 x 的状态
if (x instanceof MyPromise) {
// 2.3.2.1: 如果 x 处于等待态,promise 需保持为等待态直至 x 被拒绝或解决
// 2.3.2.2: 如果 x 处于执行态,用相同的值执行 promise
// 2.3.2.3: 如果 x 处于拒绝态,用相同的据因拒绝 promise
x.then(
y => resolvePromise(promise, y, resolve, reject),
reject
);
return;
}
// 2.3.3: 如果 x 为对象或者函数
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
let then;
try {
// 2.3.3.1: 把 x.then 赋值给 then
then = x.then;
} catch (e) {
// 2.3.3.2: 如果取 x.then 的值时抛出错误 e,则以 e 为据因拒绝 promise
return reject(e);
}
// 2.3.3.3: 如果 then 是函数
if (typeof then === 'function') {
let called = false;
try {
// 2.3.3.3.1: 调用 then 方法,x 作为 this,两个回调函数作为参数
then.call(
x,
// 2.3.3.3.2: 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
y => {
// 2.3.3.3.3: 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
if (called) return;
called = true;
resolvePromise(promise, y, resolve, reject);
},
// 2.3.3.3.4: 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
r => {
if (called) return;
called = true;
reject(r);
}
);
} catch (e) {
// 2.3.3.3.4: 如果调用 then 方法抛出了异常 e
// 2.3.3.3.4.1: 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
if (called) return;
// 2.3.3.3.4.2: 否则以 e 为据因拒绝 promise
reject(e);
}
} else {
// 2.3.3.4: 如果 then 不是函数,以 x 为参数执行 promise
resolve(x);
}
} else {
// 2.3.4: 如果 x 不为对象或者函数,以 x 为参数执行 promise
resolve(x);
}
}
class MyPromise {
/**
* Promise A+ 1: Terminology
* 构造函数,接收一个执行器函数
*/
constructor(executor) {
this.state = PENDING; // Promise 当前状态
this.value = undefined; // Promise 的终值
this.reason = undefined; // Promise 的据因
this.onFulfilledCallbacks = []; // Promise 成功时的回调队列
this.onRejectedCallbacks = []; // Promise 失败时的回调队列
/**
* Promise A+ 1.3: "fulfilled" 状态转换函数
*/
const resolve = value => {
// 2.1.1: 当状态为 pending 时才能转换为 fulfilled
if (this.state === PENDING) {
this.state = FULFILLED;
this.value = value;
// 2.2.6.1: 当 Promise 成功执行时,所有对应的 onFulfilled 回调必须按照它们对应的 then 的调用顺序来执行
this.onFulfilledCallbacks.forEach(callback => callback());
}
};
/**
* Promise A+ 1.5: "rejected" 状态转换函数
*/
const reject = reason => {
// 2.1.1: 当状态为 pending 时才能转换为 rejected
if (this.state === PENDING) {
this.state = REJECTED;
this.reason = reason;
// 2.2.6.2: 当 Promise 被拒绝执行时,所有对应的 onRejected 回调必须按照它们对应的 then 的调用顺序来执行
this.onRejectedCallbacks.forEach(callback => callback());
}
};
try {
// 立即执行 executor
executor(resolve, reject);
} catch (error) {
// 如果执行器抛出异常,Promise 应该被拒绝
reject(error);
}
}
/**
* Promise A+ 2.2: The then Method
* then 方法必须返回一个 Promise
*/
then(onFulfilled, onRejected) {
// 2.2.1: onFulfilled 和 onRejected 都是可选参数
// 2.2.7.3: 如果 onFulfilled 不是函数,它必须被忽略
// 2.2.7.4: 如果 onRejected 不是函数,它必须被忽略
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
// 2.2.7: then 必须返回一个 Promise
const promise2 = new MyPromise((resolve, reject) => {
if (this.state === FULFILLED) {
// 2.2.4: onFulfilled 或 onRejected 只有在执行环境堆栈仅包含平台代码时才可被调用
setTimeout(() => {
try {
// 2.2.2.1: onFulfilled 必须在 promise fulfilled 后执行,并以 promise 的 value 作为第一个参数
const x = onFulfilled(this.value);
// 2.2.7.1: 如果 onFulfilled 或 onRejected 返回一个值 x,则运行 Promise 解决过程 [[Resolve]](promise2, x)
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
// 2.2.7.2: 如果 onFulfilled 或 onRejected 抛出异常 e,则 promise2 必须拒绝执行,并返回拒因 e
reject(error);
}
}, 0);
} else if (this.state === REJECTED) {
setTimeout(() => {
try {
// 2.2.3.1: onRejected 必须在 promise rejected 后执行,并以 promise 的 reason 作为第一个参数
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
} else if (this.state === PENDING) {
// 2.2.6: then 可能会被同一个 promise 调用多次
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
}
});
return promise2;
}
/**
* ES6 Promise.prototype.catch
* 相当于 then(null, onRejected)
*/
catch(onRejected) {
return this.then(null, onRejected);
}
/**
* ES6 Promise.prototype.finally
* 无论 Promise 成功还是失败都会执行的回调
*/
finally(callback) {
return this.then(
value => MyPromise.resolve(callback()).then(() => value),
reason => MyPromise.resolve(callback()).then(() => { throw reason; })
);
}
/**
* ES6 Promise.resolve
* 创建一个已解决的 Promise
*/
static resolve(value) {
// 如果 value 已经是 Promise 实例,直接返回
if (value instanceof MyPromise) {
return value;
}
// 如果 value 是 thenable 对象
if (value && typeof value === 'object' && typeof value.then === 'function') {
return new MyPromise(value.then);
}
// 其他情况返回一个以 value 为终值的 Promise
return new MyPromise(resolve => resolve(value));
}
/**
* ES6 Promise.reject
* 创建一个已拒绝的 Promise
*/
static reject(reason) {
return new MyPromise((_, reject) => reject(reason));
}
/**
* ES6 Promise.all
* 等待所有 Promise 完成,或第一个 Promise 被拒绝
*/
static all(promises) {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError('Argument must be an array'));
}
if (promises.length === 0) {
return resolve([]);
}
const results = new Array(promises.length);
let completedCount = 0;
promises.forEach((promise, index) => {
MyPromise.resolve(promise).then(
value => {
results[index] = value;
completedCount++;
if (completedCount === promises.length) {
resolve(results);
}
},
reject // 任何一个 Promise 被拒绝,整个 all Promise 就被拒绝
);
});
});
}
/**
* ES6 Promise.race
* 返回第一个敲定的 Promise(无论是完成还是拒绝)
*/
static race(promises) {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError('Argument must be an array'));
}
if (promises.length === 0) {
// 空数组的 race 会永远保持 pending
return;
}
promises.forEach(promise => {
MyPromise.resolve(promise).then(resolve, reject);
});
});
}
/**
* ES2020 Promise.allSettled
* 等待所有 Promise 敲定(无论是完成还是拒绝)
*/
static allSettled(promises) {
return new MyPromise(resolve => {
if (!Array.isArray(promises)) {
return reject(new TypeError('Argument must be an array'));
}
if (promises.length === 0) {
return resolve([]);
}
const results = new Array(promises.length);
let completedCount = 0;
const checkCompletion = () => {
if (++completedCount === promises.length) {
resolve(results);
}
};
promises.forEach((promise, index) => {
MyPromise.resolve(promise).then(
value => {
results[index] = { status: FULFILLED, value };
checkCompletion();
},
reason => {
results[index] = { status: REJECTED, reason };
checkCompletion();
}
);
});
});
}
/**
* ES2021 Promise.any
* 等待第一个完成的 Promise,如果所有 Promise 都被拒绝,则聚合所有拒绝原因
*/
static any(promises) {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError('Argument must be an array'));
}
if (promises.length === 0) {
return reject(new AggregateError([], 'All promises were rejected'));
}
const errors = new Array(promises.length);
let rejectedCount = 0;
promises.forEach((promise, index) => {
MyPromise.resolve(promise).then(
resolve, // 任何一个 Promise 完成,整个 any Promise 就完成
reason => {
errors[index] = reason;
rejectedCount++;
if (rejectedCount === promises.length) {
reject(new AggregateError(errors, 'All promises were rejected'));
}
}
);
});
});
}
}
// 测试用例
if (typeof module !== 'undefined' && module.exports) {
module.exports = MyPromise;
}
// 使用示例
if (typeof window !== 'undefined') {
window.MyPromise = MyPromise;
}
MyPromise.deferred = function () {
var result = {};
result.promise = new MyPromise(function (resolve, reject) {
result.resolve = resolve;
result.reject = reject;
});
return result;
}
注意:如果想测试自己手写的MyPromise是否符合PromiseA+标准,请进行如下操作
1. 下载测试依赖
pnpm install promises-aplus-tests
2. 在MyPromise最下面补充deferred代码
MyPromise.deferred = function () {
var result = {};
result.promise = new MyPromise(function (resolve, reject) {
result.resolve = resolve;
result.reject = reject;
});
return result;
}
3. 配置脚本
"scripts": {
"test": "promises-aplus-tests MyPromise"
}
4. pnpm run test
5. 如果跑完全部通过 恭喜你实现了一个通过PromiseA+规范的MyPromise!!!