Promises/A+
在实现前先了解一下有关Promise的标准规范,以下内容摘抄自promisesaplus
一个为实现者提供健全、互操作性JavaScript Promise的开放标准。
一个promise表示异步操作的最终结果。与promise进行交互的主要方式是通过它的then
方法,该方法注册回调函数,以接收promise的最终值或无法实现promise的原因。
本规范详细说明了then
方法的行为,提供了一个所有符合Promises/A+的promise实现都可以依赖的互操作基础。因此,该规范应被认为非常稳定。尽管Promises/A+组织可能会不时地用向后兼容的小改动来修订本规范以应对新发现的问题,但我们将在经过认真考虑、讨论和测试后才整合较大的或向后不兼容的更改。
从历史上看,Promises/A+澄清了早期Promises/A提案的行为条款,将其扩展以涵盖事实上的行为,并省略了那些未详细说明或有问题的部分。
最后,核心的Promises/A+规范并未涉及如何创建、实现或拒绝承诺,而选择专注于提供一个互操作的then
方法。未来的配套规范可能会涉及这些主题。
术语
- “promise”是具有符合本规范的
then
方法的对象或函数。 - “thenable”是定义了
then
方法的对象或函数。 - “value”是任何合法的JavaScript值(包括
undefined
、thenable或promise)。 - “exception”是使用
throw
语句抛出的值。 - “reason”是表示promise被拒绝的原因的值。
要求
Promise状态
一个promise必须处于以下三种状态之一:pending(待定)、fulfilled(实现)或rejected(拒绝)。
- 处于pending状态的promise:
- 可以转换为实现或拒绝状态。
- 处于fulfilled状态的promise:
- 不能转换为其他任何状态。
- 必须具有一个值,该值不得更改。
- 处于rejected状态的promise:
- 不能转换为其他任何状态。
- 必须具有一个原因,该原因不得更改。
这里,“不得更改”意味着不可变的标识(即===
),但并不意味着深层的不可变性。
then方法
promise必须提供一个then
方法以访问其当前或最终的值或原因。
一个promise的then
方法接受两个参数:
promise.then(onFulfilled, onRejected)
onFulfilled
和onRejected
都是可选参数:- 如果
onFulfilled
不是一个函数,它必须被忽略。 - 如果
onRejected
不是一个函数,它必须被忽略。
- 如果
- 如果
onFulfilled
是一个函数:- 它必须在
promise
实现后调用,并以promise
的值作为其第一个参数。 - 在
promise
实现之前不得调用它。 - 它不能被调用多次。
- 它必须在
- 如果
onRejected
是一个函数:- 它必须在
promise
被拒绝后调用,并以promise
的原因作为其第一个参数。 - 在
promise
被拒绝之前不得调用它。 - 它不能被调用多次。
- 它必须在
onFulfilled
或onRejected
不能在执行上下文堆栈中只包含平台代码之前调用。[ 3.1]。onFulfilled
和onRejected
必须作为函数被调用(即没有this
值)。[3.2]then
方法可以在同一个promise上多次调用。- 如果/当
promise
被实现时,所有相应的onFulfilled
回调函数必须按照它们发起then
调用的顺序执行。 - 如果/当
promise
被拒绝时,所有相应的onRejected
回调函数必须按照它们发起then
调用的顺序执行。
- 如果/当
then
方法必须返回一个promise。[3.3]promise2 = promise1.then(onFulfilled, onRejected);
- 如果
onFulfilled
或onRejected
返回一个值x
,则运行Promise Resolution Procedure[[Resolve]](promise2, x)
。 - 如果
onFulfilled
或onRejected
抛出异常e
,则promise2
必须以e
作为原因被拒绝。 - 如果
onFulfilled
不是一个函数且promise1
被实现,则promise2
必须以与promise1
相同的值被实现。 - 如果
onRejected
不是一个函数且promise1
被拒绝,则promise2
必须以与promise1
相同的原因被拒绝。
- 如果
Promise解决过程
Promise解决过程是一个抽象操作,接受一个promise和一个值作为输入,我们将其表示为[[Resolve]](promise, x)
。如果x
是一个thenable,它尝试使promise
采用x
的状态,假设x
至少在某种程度上像一个promise。否则,它使用值x
来实现promise
。
对thenable的处理允许promise实现进行互操作,只要它们暴露符合Promises/A+的then
方法。它还允许Promises/A+实现通过合理的then
方法来“吸收”不符合规范的实现。
运行[[Resolve]](promise, x)
,执行以下步骤:
- 如果
promise
和x
引用同一个对象,则以TypeError
为原因拒绝promise
。 - 如果
x
是一个promise,采用其状态[3.4]:- 如果
x
处于待定状态,则promise
必须保持待定状态,直到x
被实现或拒绝。 - 如果/当
x
被实现时,用相同的值实现promise
。 - 如果/当
x
被拒绝时,用相同的原因拒绝promise
。
- 如果
- 否则,如果
x
是一个对象或函数:- 让
then
为x.then
。[3.5] - 如果获取属性
x .then
导致抛出异常e
,则以e
为原因拒绝promise
。 - 如果
then
是一个函数,则以x
作为this
,第一个参数为resolvePromise
,第二个参数为rejectPromise
调用它,其中:- 如果/当
resolvePromise
被调用并传入值y
,运行[[Resolve]](promise, y)
。 - 如果/当
rejectPromise
被调用并传入原因r
,以r
拒绝promise
。 - 如果
resolvePromise
和rejectPromise
都被调用,或者对同一个参数进行多次调用,则第一次调用优先,任何后续调用都将被忽略。 - 如果调用
then
导致抛出异常e
,- 如果已经调用了
resolvePromise
或rejectPromise
,则忽略它。 - 否则,以
e
为原因拒绝promise
。
- 如果已经调用了
- 如果/当
- 如果
then
不是一个函数,则以x
来实现promise
。
- 让
- 如果
x
不是对象或函数,则用x
来实现promise
。
如果一个promise被解决为一个参与循环thenable链的thenable,以至于递归的[[Resolve]](promise, thenable)
最终再次调用[[Resolve]](promise, thenable)
,按照上述算法进行将导致无限递归。实现可以选择性地检测到这种递归并以一个信息丰富的TypeError
为原因拒绝promise
,但不是必须的。[3.6]
注释
- 这里的“平台代码”指的是引擎、环境和promise实现的代码。实际上,这个要求确保
onFulfilled
和onRejected
在then
被调用的事件循环轮次之后以及在一个新的堆栈上异步执行。这可以通过“宏任务”机制(如setTimeout
或setImmediate
)或“微任务”机制(如MutationObserver
或process.nextTick
)来实现。由于promise实现被认为是平台代码,它本身可能包含一个任务调度队列或“跳板”,其中处理程序被调用。 - 也就是说,在严格模式下,
this
将为undefined
;在非严格模式下,它将是全局对象。 - 实现可以允许
promise2 === promise1
,前提是实现满足所有要求。每个实现应该说明它是否可以产生promise2 === promise1
以及在什么条件下。 - 通常情况下,只有当
x
来自当前实现时,才知道x
是一个真正的promise。此条款允许使用实现特定的方式来采用已知符合规范的promises的状态。 - 首先存储对
x.then
的引用,然后测试该引用,并调用该引用,避免对x.then
属性进行多次访问。这样的预防措施对于确保在存在访问器属性的情况下保持一致性非常重要,因为其值可能在检索之间发生变化。 - 实现不应对thenable链的深度设置任意限制,并假设在超过该任意限制后递归将是无限的。只有真正的循环应该导致
TypeError
;如果遇到一系列不同的thenables的无限链,无限递归是正确的行为。
具体代码
// Promise构造函数
function PromiseAPlus(executor) {
var self = this;
self.state = 'pending'; // Promise的初始状态为pending
self.value = undefined; // Promise的最终值
self.reason = undefined; // Promise的拒因
self.onFulfilledCallbacks = []; // 存储所有成功回调的数组
self.onRejectedCallbacks = []; // 存储所有失败回调的数组
function resolve(value) {
if (self.state === 'pending') {
self.state = 'fulfilled'; // 将状态改为fulfilled
self.value = value; // 存储最终值
self.onFulfilledCallbacks.forEach(function(callback) {
callback(self.value); // 执行所有成功回调
});
}
}
function reject(reason) {
if (self.state === 'pending') {
self.state = 'rejected'; // 将状态改为rejected
self.reason = reason; // 存储拒因
self.onRejectedCallbacks.forEach(function(callback) {
callback(self.reason); // 执行所有失败回调
});
}
}
try {
executor(resolve, reject); // 执行executor函数
} catch (err) {
reject(err);
}
}
// Promise原型上的then方法
PromiseAPlus.prototype.then = function(onFulfilled, onRejected) {
var self = this;
var newPromise;
// 处理onFulfilled和onRejected参数不是函数的情况
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(value) {
return value;
};
onRejected = typeof onRejected === 'function' ? onRejected : function(reason) {
throw reason;
};
if (self.state === 'fulfilled') {
// 如果Promise已经fulfilled,直接执行onFulfilled回调,并返回一个新的Promise
return newPromise = new PromiseAPlus(function(resolve, reject) {
setTimeout(function() {
try {
var x = onFulfilled(self.value);
resolvePromise(newPromise, x, resolve, reject);
} catch (err) {
reject(err);
}
}, 0);
});
}
if (self.state === 'rejected') {
// 如果Promise已经rejected,直接执行onRejected回调,并返回一个新的Promise
return newPromise = new PromiseAPlus(function(resolve, reject) {
setTimeout(function() {
try {
var x = onRejected(self.reason);
resolvePromise(newPromise, x, resolve, reject);
} catch (err) {
reject(err);
}
}, 0);
});
}
if (self.state === 'pending') {
// 如果Promise处于pending状态,将onFulfilled和onRejected回调添加到对应的数组中
return newPromise = new PromiseAPlus(function(resolve, reject) {
self.onFulfilledCallbacks.push(function(value) {
try {
var x = onFulfilled(value);
resolvePromise(newPromise, x, resolve, reject);
} catch (err) {
reject(err);
}
});
self.onRejectedCallbacks.push(function(reason) {
try {
var x = onRejected(reason);
resolvePromise(newPromise, x, resolve, reject);
} catch (err) {
reject(err);
}
});
});
}
};
// 解析Promise链式调用的结果
function resolvePromise(promise, x, resolve, reject) {
var then;
var thenCalledOrThrow = false;
if (promise === x) {
return reject(new TypeError('Chaining cycle detected for promise!'));
}
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
try {
then = x.then;
if (typeof then === 'function') {
then.call(x, function(y) {
if (thenCalledOrThrow) return;
thenCalledOrThrow = true;
resolvePromise(promise, y, resolve, reject);
}, function(err) {
if (thenCalledOrThrow) return;
thenCalledOrThrow = true;
reject(err);
});
} else {
resolve(x);
}
} catch (err) {
if (thenCalledOrThrow) return;
thenCalledOrThrow = true;
reject(err);
}
} else {
resolve(x);
}
}
// 测试示例
var promise = new PromiseAPlus(function(resolve, reject) {
setTimeout(function() {
resolve('Success!');
}, 1000);
});
promise.then(function(value) {
console.log(value); // 输出: Success!
});
文章评论