Promises/A+
概念
Promise States
一个 promise 必须处于以下三种状态之一:pending(待定)、fulfilled(已解决)或 rejected(已拒绝)。
- 当 promise 处于
pending状态时:- 可以转换为
fulfilled或rejected状态。
- 可以转换为
- 当 promise 处于
fulfilled状态时:- 不得转换为其他状态。
- 必须有一个值,并且该值不能改变。
- 当 promise 处于
rejected状态时:- 不得转换为其他状态。
- 必须有一个原因,并且该原因不能改变。
The then Method
Promise 必须提供一个 then 方法来访问它当前或将来的值或原因。
Promise 的 then 方法接受两个参数:
promise.then(onFulfilled, onRejected);
onFulfilled和onRejected都是可选参数:- 如果
onFulfilled不是一个函数,它必须被忽略。 - 如果
onRejected不是一个函数,它必须被忽略。
- 如果
如果
onFulfilled是一个函数:- 它必须在 promise 被解决(fulfilled)后调用,并将 promise 的值作为它的第一个参数。
- 它不能在 promise 被解决之前调用。
- 它不能被调用多次。
如果
onRejected是一个函数:- 它必须在 promise 被拒绝(rejected)后调用,并将 promise 的原因作为它的第一个参数。
- 它不能在 promise 被拒绝之前调用。
- 它不能被调用多次。
onFulfilled或onRejected不应被调用,直到执行上下文栈中只包含平台代码。[3.1]onFulfilled和onRejected必须作为函数调用(即没有绑定this值)。[3.2]then可以在同一个 promise 上多次调用。- 当 promise 被解决时,所有相关的
onFulfilled回调必须按照它们最初调用then的顺序执行。 - 当 promise 被拒绝时,所有相关的
onRejected回调必须按照它们最初调用then的顺序执行。
- 当 promise 被解决时,所有相关的
then必须返回一个 promisepromise2 = promise1.then(onFulfilled, onRejected);- 如果
onFulfilled或onRejected返回一个值x,则执行 Promise 解析过程[[Resolve]](promise2, x)。 - 如果
onFulfilled或onRejected抛出异常e,promise2必须以e作为原因被拒绝。 - 如果
onFulfilled不是一个函数,并且promise1已经解决(fulfilled),则promise2必须以与promise1相同的值被解决。 - 如果
onRejected不是一个函数,并且promise1已经被拒绝(rejected),则promise2必须以与promise1相同的原因被拒绝。
- 如果
The Promise Resolution Procedure
Promise 解析过程是一个抽象操作,它接受一个 promise 和一个值作为输入,我们将其表示为 [[Resolve]](promise, x)。如果 x 是一个 thenable 对象,它会尝试让 promise 采用 x 的状态,假设 x 至少在某种程度上表现得像一个 promise。否则,它将使用值 x 来完成 promise。
要运行 [[Resolve]](promise, x),执行以下步骤:
如果
promise和x指向同一个对象,则以 TypeError 作为原因拒绝promise。如果
x是一个 promise,采用它的状态:- 如果
x处于待定(pending)状态,则promise必须保持待定状态,直到x被解决(fulfilled)或拒绝(rejected)。 - 如果/当
x被解决时,用相同的值解决promise。 - 如果/当
x被拒绝时,用相同的原因拒绝promise。
- 如果
否则,如果
x是一个对象或函数:取
x.then的值如果获取
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成功的value
如果
x不是一个对象或函数,使用x作为promise成功的value
源码实现
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";
function promiseResolve(promise, x, resolve, reject) {
if (promise === x) {
return reject(new TypeError("Chaining cycle detected for promise"));
}
if (x instanceof Promise) {
x.then(resolve, reject);
} else if (x !== null && (typeof x === "object" || typeof x === "function")) {
let then;
let called = false;
try {
then = x.then;
} catch (e) {
if (called) return;
called = true;
return reject(e);
}
if (typeof then === "function") {
try {
then.call(
x,
(y) => {
if (called) return;
called = true;
setTimeout(() => {
promiseResolve(promise, y, resolve, reject);
});
},
(r) => {
if (called) return;
called = true;
reject(r);
}
);
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
if (called) return;
called = true;
resolve(x);
}
} else {
resolve(x);
}
}
class MyPromise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallback = [];
this.onRejectedCallback = [];
const resolve = (value) => {
if (value instanceof Promise) {
// 递归解析的流程
return value.then(resolve, reject);
}
if (this.status === PENDING) {
this.value = value;
this.status = FULFILLED;
this.onFulfilledCallback.forEach((callback) => callback());
}
};
const reject = (reason) => {
if (this.status === PENDING) {
this.reason = reason;
this.status = REJECTED;
this.onRejectedCallback.forEach((callback) => callback());
}
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
static deferred() {
var result = {};
result.promise = new MyPromise(function (resolve, reject) {
result.resolve = resolve;
result.reject = reject;
});
return result;
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v;
onRejected =
typeof onRejected === "function"
? onRejected
: (e) => {
throw e;
};
let promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
promiseResolve(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
const x = onRejected(this.reason);
promiseResolve(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
if (this.status === PENDING) {
this.onFulfilledCallback.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
promiseResolve(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
this.onRejectedCallback.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.reason);
promiseResolve(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
}
});
return promise2;
}
static resolve(value) {
return new MyPromise((resolve, reject) => {
resolve(value);
});
}
}
module.exports = MyPromise;