前言
Promise 是一种异步编程的解决方案,可以使我们的代码更简单整洁。了解 Promise 原理可以帮助我们快速分析定位问题。本篇将带你从零到一逐步实现 Promise/A+ 规范。
初步实现
我们先来简单回顾一下 Promise 使用方式:
const p = new Promise(() => {
console.log("1");
});
console.log("p: ", p);
//1
//p1: Promise { <pending> }
从这个例子中,我们可以看到:
- 可以通过
new Promise来实例化 promise 对象。 - 实例化时需传入执行器函数(executor)作为参数,执行器函数会立即调用。
如果不传递执行器函数则会报
TypeError
现在我们可以尝试写第一版了,为了避免与原生 Promise 冲突,我把我们实现的 Promise 命名为 Eventual。代表相同概念的术语还有 Delay、Deferred、Future 等等,具体名字你可以按照自己的想法。
class Eventual {
constructor(executor) {
//实例化时会立即执行
executor();
}
}
const eventual = new Eventual(() => {
console.log("1");
});
//1
//eventual: Eventual {}
现在我们得到了一个 eventual 实例,传入的执行器函数也会立即执行,但仔细观察就会发现 eventual 是个空对象并没有任何状态这与我们的预期不符。
状态管理
Promise/A+ 规定一个 promise 必然处于以下三种状态之一:
- 待定(Pending):初始状态,既没有被兑现,也没有被拒绝。
- 已兑现(Fulfilled):意味着操作成功完成。
- 已拒绝(Rejected):意味着操作失败。
状态只能由 Pending --> Fulfilled 或者 Pending --> Rejected。Fulfilled和Rejected不能相互切换,当然也不能在切换为Pending。也就是说状态切换是单向且不可逆的。
接下来我们来完善第一版:
class Eventual {
static PENDING = "pending";
static FULFILLED = "fulfilled";
static REJECTED = "rejected";
//设置初始状态
PromiseState = Eventual.PENDING;
constructor(executor) {
//实例化时会立即执行
executor();
}
}
const eventual = new Eventual(() => {
console.log("1");
});
//1
//eventual: Eventual { PromiseState: 'pending' }
我们定义了 3 种状态并使用 PromiseState 保存初始状态。
状态切换
现在 eventual 有了初始状态那么怎么来切换 promise 的状态呢?你可能会脱口而出 eventual.PromiseState = xxx 。原生中PromiseState是私有的只能在内部操作,来看看原生是怎么切换状态的:
const p1 = new Promise((resolve, reject) => {
resolve();
});
console.log("p1", p1); //Promise {<fulfilled>: undefined}
const p2 = new Promise((resolve, reject) => {
reject();
});
console.log("p2", p2); //Promise {<rejected>: undefined}
控制 promise 状态的切换是通过执行器的参数 resolve、reject 实现的。
调用 resolve 会把状态切换为 Fulfilled,调用 reject 会把状态切换为 Rejected。另外调用 reject 也会抛出错误。
状态切换的实现如下:
class Eventual {
static PENDING = "pending";
static FULFILLED = "fulfilled";
static REJECTED = "rejected";
//设置初始状态
PromiseState = Eventual.PENDING;
resolve = () => {
if (this.PromiseState === Eventual.PENDING) {
//切换状态为Fulfilled
this.PromiseState = Eventual.FULFILLED;
}
};
reject = () => {
if (this.PromiseState === Eventual.PENDING) {
//切换状态为Rejected
this.PromiseState = Eventual.REJECTED;
}
};
constructor(executor) {
//实例化时会立即执行
executor(this.resolve, this.reject);
}
}
这里之所以使用箭头函数实现 resolve、reject 是因为如果是普通函数this 会指向 window(严格模式下是 undefined),会出现this指向错误。
接下来测试一下:
const p1 = new Eventual((resolve, reject) => {
resolve();
});
console.log("p1", p1);
const p2 = new Eventual((resolve, reject) => {
reject();
});
console.log("p2: ", p2);
const p3 = new Eventual((resolve, reject) => {
resolve();
reject();
});
console.log("p3: ", p3);
输出:
p1 Eventual {
PromiseState: 'fulfilled',
resolve: [Function: resolve],
reject: [Function: reject]
}
p2: Eventual {
PromiseState: 'rejected',
resolve: [Function: resolve],
reject: [Function: reject]
}
p3: Eventual {
PromiseState: 'fulfilled',
resolve: [Function: resolve],
reject: [Function: reject]
}
结果管理
待定状态的 promise 对象要么会通过一个值被兑现,要么会通过一个原因(错误)被拒绝。默认值是 undefined。
const p1 = new Promise((resolve, reject) => {
resolve("success");
});
console.log("p1", p1); //p1 Promise {<fulfilled>: 'success'}
const p2 = new Promise((resolve, reject) => {
reject("error");
});
console.log("p2", p2); //p2 Promise {<rejected>: 'error'}
被兑现的值或被拒绝的原因可以通过 resolve、reject 保存到 promise 的 PromiseResult 上以供将来then注册的后续处理程序所使用。和 PromiseState相同PromiseResult也是私有属性。
接下来实现结果管理:
class Eventual {
static PENDING = "pending";
static FULFILLED = "fulfilled";
static REJECTED = "rejected";
//设置初始状态
PromiseState = Eventual.PENDING;
//保存被兑现的值或者被拒绝的理由默认为undefined
PromiseResult = undefined;
resolve = (value) => {
if (this.PromiseState === Eventual.PENDING) {
this.PromiseState = Eventual.FULFILLED;
//保存兑现的值
this.PromiseResult = value;
}
};
reject = (reason) => {
if (this.PromiseState === Eventual.PENDING) {
this.PromiseState = Eventual.REJECTED;
//保存被拒绝的原因
this.PromiseResult = reason;
}
};
constructor(executor) {
//实例化时会立即执行
executor(this.resolve, this.reject);
}
}
我们再来测试一下:
const p1 = new Eventual((resolve, reject) => {
resolve("success");
});
console.log("p1", p1);
const p2 = new Eventual((resolve, reject) => {
reject("error");
});
console.log("p2: ", p2);
输出:
p1: Eventual {
PromiseState: 'fulfilled',
PromiseResult: 'success',
resolve: [Function: resolve],
reject: [Function: reject]
}
p2: Eventual {
PromiseState: 'rejected',
PromiseResult: 'error',
resolve: [Function: resolve],
reject: [Function: reject]
}
到目前为止基础实现就算完成啦 😀。
then
按照传统我们来回顾一下 then的使用方式:
const p1 = new Promise((resolve, reject) => {
resolve("success");
});
p1.then((value) => {
console.log(value);
});
const p2 = new Promise((resolve, reject) => {
reject("error");
});
p2.then(undefined, (reason) => {
console.log(reason);
});
//success
//error
then 是用来为 promise 注册后续的处理程序,该方法最多接受两个参数 onFulfilled 和 onRejected。这两个函数都是可选的,如果提供则会在 promise 被兑现或被拒绝时执行。
onFulfilled 和 onRejected
因为 onFulfilled 只会在 promise 被兑现时执行,所以只要判断当前 promise 处于相应的状态去执行 onFulfilled就好了。onRejected与之类似不多做赘述。
class Eventual {
then(onFulfilled, onRejected) {
//判断当前的状态如果是Fulfilled则执行注册的onFulfilled函数
if (this.PromiseState === Eventual.FULFILLED) {
onFulfilled(this.PromiseResult);
}
//判断当前的状态如果是Rejected则执行注册的onRejected函数
if (this.PromiseState === Eventual.REJECTED) {
onRejected(this.PromiseResult);
}
}
constructor(executor) {
//实例化时会立即执行
executor(this.resolve, this.reject);
}
}
我们来测试一下:
const p1 = new Eventual((resolve, reject) => {
resolve("success");
});
p1.then((value) => {
console.log(value);
});
const p2 = new Eventual((resolve, reject) => {
reject("error");
});
p2.then(undefined, (reason) => {
console.log(reason);
});
//success
//error
then 注册的回调被成功打印了出来。真是太对了哥,哥太对 😛。
回调保存
你以为上面的代码就没问题了吗 🧐?如果 resolve 是放在异步里执行的会怎样?
const eventual = new Eventual((resolve, reject) => {
setTimeout(resolve, 1000, "success");
});
eventual.then((value) => {
console.log(value);
});
执行上面的代码你会发现,居然啥也没有输出,来看看原生的行为:
const p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, "success");
});
p1.then((value) => {
console.log(value);
});
//success
两者的表现并不一致,我们的代码哪里出了问题?我们来简单分析下:
-
当实例化时执行器函数会立即调用。执行器会创建一个定时器并在 1s 后把 eventual 状态切换为 Fulfilled。
-
接着会执行 then 方法。该方法会在内部判断当前 eventual 的状态。此时 eventual 还处于 Pending ,onFulfilled 并不会执行。
-
1s 之后 resolve 将 eventual 切换为 Fulfilled。
到这里代码就执行完了,然而当状态切换为 Fulfilled 时并没有任何地方去调用 onFulfilled 自然也就什么都不会输出了。
现在问题定位了,如何解决呢 🤔。解决方法也比较简单,当 then 执行时如果 eventual 还处于 Pending 就把回调保存起来等到 resolve 时再去调用就好了
class Eventual {
//设置初始状态
PromiseState = Eventual.PENDING;
//保存被兑现的值或者被拒绝的理由默认为undefined
PromiseResult = undefined;
// 保存onFulfilled
onFulfilledCallbacks = undefined;
// 保存onRejected
onRejectedCallbacks = undefined;
resolve = (value) => {
if (this.PromiseState === Eventual.PENDING) {
this.PromiseState = Eventual.FULFILLED;
//修改为兑现的值
this.PromiseResult = value;
this.onRejectedCallbacks && this.onRejectedCallbacks(value);
}
};
reject = (reason) => {
if (this.PromiseState === Eventual.PENDING) {
this.PromiseState = Eventual.REJECTED;
//修改为被拒绝的理由
this.PromiseResult = reason;
this.rejectedCallback && this.rejectedCallback(reason);
}
};
then(onFulfilled, onRejected) {
//...部分省略
if (this.PromiseState === Eventual.PENDING) {
this.onFulfilledCallbacks = onFulfilled;
this.onRejectedCallbacks = onRejected;
}
}
constructor(executor) {
//实例化时会立即执行
executor(this.resolve, this.reject);
}
}
我们再来以第一个例子测试一下输出如下:
success
then 方法的多次调用
熟悉 promise 的同学知道,可以针对一个 promise 多次调用 then。
举个例子:
const eventual = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("success");
}, 2000);
});
eventual.then((value) => {
console.log(1);
console.log("resolve", value);
});
eventual.then((value) => {
console.log(2);
console.log("resolve", value);
});
eventual.then((value) => {
console.log(3);
console.log("resolve", value);
});
上面的代码会输出
1
value success
2
value success
3
value success
而我们的代码显然是不支持的,我们需要做点小小的改动去支持多次调用 then。
class Eventual {
static PENDING = "pending";
static FULFILLED = "fulfilled";
static REJECTED = "rejected";
//设置初始状态
PromiseState = Eventual.PENDING;
//保存被兑现的值或者被拒绝的理由默认为undefined
PromiseResult = undefined;
// 保存onFulfilled
onFulfilledCallbacks = [];
// 保存onRejected
onRejectedCallbacks = [];
resolve = (value) => {
if (this.PromiseState === Eventual.PENDING) {
this.PromiseState = Eventual.FULFILLED;
this.PromiseResult = value;
this.onFulfilledCallbacks &&
this.onFulfilledCallbacks.map((onFulfilled) => onFulfilled(value));
}
};
reject = (reason) => {
if (this.PromiseState === Eventual.PENDING) {
this.PromiseState = Eventual.REJECTED;
this.PromiseResult = reason;
this.onRejectedCallbacks &&
this.onRejectedCallbacks((onRejected) => onRejected(reason));
}
};
then(onFulfilled, onRejected) {
//...部分省略
if (this.PromiseState === Eventual.PENDING) {
this.onFulfilledCallbacks.push(onFulfilled);
this.onRejectedCallbacks.push(onRejected);
}
}
constructor(executor) {
//实例化时会立即执行
executor(this.resolve, this.reject);
}
}
接下来测试一下:
const eventual = new Eventual((resolve, reject) => {
setTimeout(() => {
resolve("success");
}, 2000);
});
eventual.then((value) => {
console.log(1);
console.log("value", value);
});
eventual.then((value) => {
console.log(2);
console.log("value", value);
});
eventual.then((value) => {
console.log(3);
console.log("value", value);
});
输出:
1
value success
2
value success
3
value success
所有 then 中的回调函数都已经执行,已经实现then 方法的多次调用。
参数校验
前面我们提到了 then 接受两个函数作为参数。如果我们非要传递一个非函数类型会怎样?
举个例子:
const p = new Promise((resolve, reject) => {
resolve("success");
});
p.then("str");
执行上面代码你会发现浏览器根本不甩你。
根据规范传递给 then 的任何非函数类型都会静默忽略。所谓忽略并不是什么都不做,对于 onFulfilled 会 return value 对于 onRejected 会 throw reason。
class Eventual {
then(onFulfilled, onRejected) {
onFulfilled =
typeof onFulfilled === "function" ? onFulfilled : (value) => value;
onRejected =
typeof onRejected === "function"
? onRejected
: (reason) => {
throw reason;
};
}
}
现在参数校验也实现了,距离完整版又进了一大步。
异步实现
接下来我们从一道题切入来看看 promise 的执行顺序。
console.log(1);
let promise = new Promise((resolve, reject) => {
console.log(2);
resolve("3");
});
promise.then(
(value) => {
console.log(value);
},
(reason) => {
console.log(reason);
}
);
console.log(4);
上面的代码直观感觉应该是输出:
1
2
3
4
实际上却会输出:
1
2
4
3
造成这么违反直觉的原因是,onFulfilled 和 onRejected 必须要等到当前执行栈清空后才能被调用,换句话说它们是异步执行的。
Promise/A+ 规定了,实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。这个事件队列可以采用宏任务(macro-task)机制或者微任务(micro-task)机制来实现。事实上原生 promise 是通过微任务实现的。
接下来实现异步:
class Eventual {
then(onFulfilled, onRejected) {
if (this.PromiseState === Eventual.FULFILLED) {
queueMicrotask(() => {
onFulfilled(this.PromiseResult);
});
} else if (this.PromiseState === Eventual.REJECTED) {
queueMicrotask(() => {
onRejected(this.PromiseResult);
});
} else if (this.PromiseState === Eventual.PENDING) {
this.onFulfilledCallbacks.push(() => {
queueMicrotask(() => {
onFulfilled(this.PromiseResult);
});
});
this.onRejectedCallbacks.push(() => {
queueMicrotask(() => {
onRejected(this.PromiseResult);
});
});
}
}
}
我们在用上面的例子测试一下:
1
2
4
3
最后输出和原生 promise 一致。
链式调用
什么是链式调用呢举个例子:
const p = new Promise((resolve, reject) => {});
p.then(() => {
console.log(1);
}).then(() => {
console.log(2);
});
像上面这样可以new promise().then().then()这样的就称为链式调用。现在我们的代码是不支持链式调用的,因为我们在 then 里面没有返回任何东西。
Promise/A+ 2.2.7规定:
promise2 = promise1.then(onFulfilled, onRejected);
then 方法必须返回一个 promise 对象,这是实现链式调用的核心
根据 2.2.7 规范,我们来实现链式调用:
class Eventual {
then(onFulfilled, onRejected) {
return new Eventual((resolve, reject) => {
if (this.PromiseState === Eventual.FULFILLED) {
queueMicrotask(() => {
onFulfilled(this.PromiseResult);
});
} else if (this.PromiseState === Eventual.REJECTED) {
queueMicrotask(() => {
onRejected(this.PromiseResult);
});
} else if (this.PromiseState === Eventual.PENDING) {
this.onFulfilledCallbacks.push(() => {
queueMicrotask(() => {
onFulfilled(this.PromiseResult);
});
});
this.onRejectedCallbacks.push(() => {
queueMicrotask(() => {
onRejected(this.PromiseResult);
});
});
}
});
}
}
我们在 then 方法里面返回了一个 eventual 实例,这样就可以链式的注册后续处理函数。
虽然可以链式的注册后续处理函数,但是目前链式注册的回调通通都不会执行。因为我们在 then 里返回了新的 eventual 实例,这些实例都是 Pending 状态。链式注册的回调会注册在新的 eventual 实例上。状态不改变 onFulfilled、onRejected 自然也不会执行。
若想要 onFulfilled 执行 只需要把新的 promise 状态切换就可以了:
class Eventual {
then(onFulfilled, onRejected) {
return new Eventual((resolve, reject) => {
if (this.PromiseState === Eventual.FULFILLED) {
queueMicrotask(() => {
onFulfilled(this.PromiseResult);
resolve();
});
} else if (this.PromiseState === Eventual.REJECTED) {
queueMicrotask(() => {
onRejected(this.PromiseResult);
resolve();
});
} else if (this.PromiseState === Eventual.PENDING) {
this.onFulfilledCallbacks.push(() => {
queueMicrotask(() => {
onFulfilled(this.PromiseResult);
resolve();
});
});
this.onRejectedCallbacks.push(() => {
queueMicrotask(() => {
onRejected(this.PromiseResult);
resolve();
});
});
}
});
}
}
我们来测试一下:
const p = new Eventual((resolve) => {
resolve("success");
});
p.then(() => {
console.log(1);
}).then(() => {
console.log(2);
});
//1
//2
我们来分析一下:
-
实例化 eventual 时会立即调用执行器,执行 resolve 函数将 p 切换为 Fulfilled。
-
接下来运行第一个 then 此方法是同步的,但是注册的方法是异步的。纵使现在 p 已然是 Fulfilled 也要等到全局代码执行完毕才能去执行注册的回调。then 会返回一个新的 eveventual 实例,状态为 Pending。
-
接下来为新的 eveventual 注册回调。
-
全局代码执行完毕,之后会取出第一个 then 注册的回调执行。
queueMicrotask(() => {
onFulfilled(this.PromiseResult);
resolve();
});
因为闭包的关系,可以在微任务中拿到新的 eventual 的 resolve 方法。将新的 eventual 状态切换为 Fulfilled,那么第二个 then 注册的 onFulfilled 就也会执行了。 实际上这部分工作是在 onFulfilled、onRejected 的返回值处理中进行的。
resolvePromise
关于 onFulfilled 和 onRejected 返回值的处理 Promise/A+ 洋洋洒洒写了一大片总结一下就是:
- 2.3.1 如果
promise和x指向同一对象,以TypeError为据因拒绝执行promise
/**
*
* @param {promise} promise2 promise1.then方法返回的新的promise对象
* @param {[type]} x promise1中onFulfilled或onRejected的返回值
* @param {[type]} resolve promise2的resolve方法
* @param {[type]} reject promise2的reject方法
*/
function resolvePromise(promise2, x, resolve, reject) {
if (x === promise2) {
return reject(new TypeError("Chaining cycle detected for promise"));
}
}
返回自己会造成循环引用。
2.3.3如果x为对象或者函数- 把
x.then赋值给then,如果读取发生异常,则直接让promise失败,失败原因就是抛出的错误,然后结束处理即可 - 如果
then是函数,将x作为函数的作用域this调用之 - 当
onFulfilled执行时,如果得到的数据是y,则执行[[Resolve]](promise, y) - 当
onRejected执行时,如果得到的失败原因是error,则让promise变成失败状态,原因是error - 如果
resolvePromise和rejectPromise均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
- 把
function resolvePromise(promise2, x, resolve, reject) {
if (x === promise2) {
return reject(new TypeError("Chaining cycle detected for promise"));
}
if ((typeof x === "object" && x != null) || typeof x === "function") {
let then;
let called = false;
try {
// 把 x.then 赋值给 then
then = x.then;
} catch (error) {
// 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
return reject(error);
}
// 如果 then 是函数
if (typeof then === "function") {
// 将 x 作为函数的作用域 this 调用之
// 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
then.call(
x,
(y) => {
// 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
// 如果 resolvePromise 和 rejectPromise 均被调用,
// 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
if (called) return;
called = true;
resolve(promise2, y, resolve, reject);
},
(r) => {
// 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
if (called) return;
called = true;
reject(r);
}
);
}
}
}
2.3.4如果x不为对象或者函数,以x为参数执行 promise
function resolvePromise(promise2, x, resolve, reject) {
if (x === promise2) {
return reject(new TypeError("Chaining cycle detected for promise"));
}
if ((typeof x === "object" && x != null) || typeof x === "function") {
let then;
let called = false;
try {
// 把 x.then 赋值给 then
then = x.then;
} catch (error) {
// 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
return reject(error);
}
// 如果 then 是函数
if (typeof then === "function") {
// 将 x 作为函数的作用域 this 调用之
// 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
then.call(
x,
(y) => {
// 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
// 如果 resolvePromise 和 rejectPromise 均被调用,
// 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
if (called) return;
called = true;
resolve(promise2, y, resolve, reject);
},
(r) => {
// 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
if (called) return;
called = true;
reject(r);
}
);
}
} else {
//如果 x 不为对象或者函数,以 x 为参数执行 promise
resolve(x);
}
}
2.3.3.3.4.2 如果调用 then 方法抛出了异常 e ,如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之。否则以 e 为据因拒绝 promise
function resolvePromise(promise2, x, resolve, reject) {
// 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
if (promise2 === x) {
return reject(
new TypeError("The promise and the return value are the same")
);
}
if ((typeof x === "object" && x !== null) || typeof x === "function") {
let then;
try {
// 把 x.then 赋值给 then
then = x.then;
} catch (error) {
// 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
return reject(error);
}
// 如果 then 是函数
if (typeof then === "function") {
let called = false;
// 将 x 作为函数的作用域 this 调用之
// 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
try {
then.call(
x,
// 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
(y) => {
// 如果 resolvePromise 和 rejectPromise 均被调用,
// 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
// 实现这条需要前面加一个变量called
if (called) return;
called = true;
resolvePromise(promise, y, resolve, reject);
},
// 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
(r) => {
if (called) return;
called = true;
reject(r);
}
);
} catch (error) {
// 如果调用 then 方法抛出了异常 e:
// 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
if (called) return;
// 否则以 e 为据因拒绝 promise
reject(error);
}
}
} else {
// 如果 x 不为对象或者函数,以 x 为参数执行 promise
resolve(x);
}
}
2.3.3.4 如果 then 不是函数,以 x 为参数执行 promise
function resolvePromise(promise2, x, resolve, reject) {
// 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
if (promise2 === x) {
return reject(
new TypeError("The promise and the return value are the same")
);
}
if ((typeof x === "object" && x !== null) || typeof x === "function") {
let then;
try {
// 把 x.then 赋值给 then
then = x.then;
} catch (error) {
// 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
return reject(error);
}
// 如果 then 是函数
if (typeof then === "function") {
let called = false;
// 将 x 作为函数的作用域 this 调用之
// 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
try {
then.call(
x,
// 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
(y) => {
// 如果 resolvePromise 和 rejectPromise 均被调用,
// 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
// 实现这条需要前面加一个变量called
if (called) return;
called = true;
resolvePromise(promise, y, resolve, reject);
},
// 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
(r) => {
if (called) return;
called = true;
reject(r);
}
);
} catch (error) {
// 如果调用 then 方法抛出了异常 e:
// 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
if (called) return;
// 否则以 e 为据因拒绝 promise
reject(error);
}
} else {
resolve(x);
}
} else {
// 如果 x 不为对象或者函数,以 x 为参数执行 promise
resolve(x);
}
}
异常处理
我们先来看看异常处理,根据 2.2.7.2 规定。
如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e。
class Eventual {
then(onFulfilled, onRejected) {
return new Eventual((resolve, reject) => {
if (this.PromiseState === Eventual.FULFILLED) {
queueMicrotask(() => {
try {
onFulfilled(this.PromiseResult);
} catch (error) {
reject(error);
}
});
} else if (this.PromiseState === Eventual.REJECTED) {
queueMicrotask(() => {
try {
onRejected(this.PromiseResult);
} catch (error) {
reject(error);
}
});
} else if (this.PromiseState === Eventual.PENDING) {
this.onFulfilledCallbacks.push(() => {
queueMicrotask(() => {
try {
onFulfilled(this.PromiseResult);
} catch (error) {
reject(error);
}
});
});
this.onRejectedCallbacks.push(() => {
queueMicrotask(() => {
try {
onFulfilled(this.PromiseResult);
} catch (error) {
reject(error);
}
});
});
}
});
}
}
顺便处理一下执行器抛出错误的情况:
class Eventual {
constructor(executor) {
try {
executor(this.resolve, this.reject);
} catch (error) {
this.reject(error);
}
}
}
完整的 Promises/A+ 实现
到这里我们的 Eventual 已经完成了 Promises/A+规范
class Eventual {
static PENDING = "pending";
static FULFILLED = "fulfilled";
static REJECTED = "rejected";
PromiseState = Eventual.PENDING;
PromiseResult = undefined;
// 存储成功回调函数
onFulfilledCallbacks = [];
// 存储失败回调函数
onRejectedCallbacks = [];
resolve = (value) => {
if (this.PromiseState === Eventual.PENDING) {
//切换状态为Fulfilled
this.PromiseState = Eventual.FULFILLED;
this.PromiseResult = value;
//执行onFulfilled回调
this.onFulfilledCallbacks &&
this.onFulfilledCallbacks.map((onFulfilled) => onFulfilled(value));
}
};
reject = (reason) => {
if (this.PromiseState === Eventual.PENDING) {
//切换状态为Rejected
this.PromiseState = Eventual.REJECTED;
this.PromiseResult = reason;
//执行onRejected回调
this.onRejectedCallbacks &&
this.onRejectedCallbacks.map((onRejected) => onRejected(reason));
}
};
then(onFulfilled, onRejected) {
//非函数类型静默忽略
onFulfilled =
typeof onFulfilled === "function" ? onFulfilled : (value) => value;
onRejected =
typeof onRejected === "function"
? onRejected
: (reason) => {
throw reason;
};
const promise2 = new Eventual((resolve, reject) => {
let x;
if (this.PromiseState === Eventual.FULFILLED) {
//异步执行
queueMicrotask(() => {
//异常处理
try {
x = onFulfilled(this.PromiseResult);
//onFulfilled返回值处理
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
} else if (this.PromiseState === Eventual.REJECTED) {
queueMicrotask(() => {
try {
x = onRejected(this.PromiseResult);
//onRejected返回值处理
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
} else if (this.PromiseState === Eventual.PENDING) {
this.onFulfilledCallbacks.push(() => {
queueMicrotask(() => {
try {
x = onFulfilled(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
});
this.onRejectedCallbacks.push(() => {
queueMicrotask(() => {
try {
x = onRejected(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
});
}
});
return promise2;
}
constructor(executor) {
//执行器函数异常处理
try {
executor(this.resolve, this.reject);
} catch (error) {
this.reject(error);
}
}
}
function resolvePromise(promise, x, resolve, reject) {
// 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
if (promise === x) {
return reject(
new TypeError("The promise and the return value are the same")
);
}
if ((typeof x === "object" && x !== null) || typeof x === "function") {
let then;
try {
// 把 x.then 赋值给 then
then = x.then;
} catch (error) {
// 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
return reject(error);
}
// 如果 then 是函数
if (typeof then === "function") {
let called = false;
// 将 x 作为函数的作用域 this 调用之
// 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
try {
then.call(
x,
// 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
(y) => {
// 如果 resolvePromise 和 rejectPromise 均被调用,
// 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
if (called) return;
called = true;
resolvePromise(promise, y, resolve, reject);
},
// 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
(r) => {
if (called) return;
called = true;
reject(r);
}
);
} catch (error) {
// 如果调用 then 方法抛出了异常 e:
// 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
if (called) return;
// 否则以 e 为据因拒绝 promise
reject(error);
}
} else {
// 如果 then 不是函数,以 x 为参数执行 promise
resolve(x);
}
} else {
// 如果 x 不为对象或者函数,以 x 为参数执行 promise
resolve(x);
}
}
Promise A+ 测试
如何证明我们写的 Eventual 符合 Promises/A+ 规范呢?当然是用promises-aplus-tests 测试一下。
安装 promises-aplus-tests:
npm install promises-aplus-tests -D
手写代码中加入 deferred:
Eventual.deferred = function () {
var result = {};
result.promise = new Eventual(function (resolve, reject) {
result.resolve = resolve;
result.reject = reject;
});
return result;
};
配置启动命令:
{
"scripts": {
"test": "promises-aplus-tests index"
}
}
开始测试:
npm run test
最终结果:
总结
总什么结啊,累了毁灭吧。