promise-polyfill-ts v1.0.1
经常会有面试官问道“如何手动实现一个符合Promise/A+规范的简易版Promise”。当然通过面试不是我们的最终目的,熟练掌握其内部实现原理才是根本。
在写代码之前,我们需要知道Promise的三种状态,即pending, resoled, rejected
。整个Promise的初始状态是pending
,然后根据后续的处理情况状态会变为resoled或者rejected
enum Status {
PENDING,
RESOLVED,
REJECTED
}
首先我们要弄清楚Promise的调用过程,在创建Promise的过程时,我们通常会new Promise(executor)
。整个Promise的构造函数接受一个executor函数,该函数接收两个参数一个是resolve,一个是reject。
但是经过简单的分析我们可以知道resolve和reject也是两个函数,并且这两个函数应该是Promise类内部帮我们实现好的。
下面我们来看Promise内部需要哪些属性:
- 我们需要一个存储当前Promise状态的status
- 一个保存Promise成功时的值,方便我们传给resolve函数
- 一个保存Promise失败时的值,方便我们传给reject函数
- 一个存储Promise成功时回调函数的数组
- 一个存储Promise失败时的回调函数的数组
private state: Status = Status.PENDING;
private value: V = null;
private reason: R = null;
private resolvedCallbacks: IStatusFunc<V>[] = [];
private rejectedCallbacks: IStatusFunc<R>[] = [];
我们也需要定义Promise内部的resolve和reject函数,这两个函数即分别执行成功和失败的回调函数。
// 将当前Promise的状态修改为resolved
// 并执行resolve时的回调函数
let resolve: IStatusFunc<V> = (value) =>{
if (this.state === Status.PENDING) {
this.state = Status.RESOLVED;
this.value = value;
this.resolvedCallbacks.forEach(callback => {
callback(value);
});
}
}
// 将Promise的状态修改为rejected
// 并执行reject时的回调函数
let reject: IStatusFunc<R> = (reason) => {
if (this.state === Status.PENDING) {
this.state = Status.REJECTED;
this.reason = reason;
this.rejectedCallbacks.forEach(callback => {
callback(reason);
});
}
}
接下来就是实现Promise中最为重要的一步,then方法,它可以实现链式调用。我们知道在jQuery中,链式调用的方式是在方法的尾部返回jQuery对象this。但显然这种方式在Promise中行不通,因为Promise的主要任务是解决异步操作,异步操作里面的return是无效的。
我们首先想到的就是在then中返回一个Promise对象,这样返回的Promise就可以继续调用其原型链上的then方法。但此时,如果单纯的执行onFullfilled
或者onRejected
回调函数的话,此时是不会有resolve或者reject这个过程的,所以为了让Promise有链式调用的功能,我们就需要有一个专门解析onFullFilled
和onRejected
返回值得函数
/**
* 解析onFullfilled或者onRejected的返回值
* @param promise2 then函数返回的新的Promise
* @param x onFullfilled或者onRejected的返回值
* @param resolve 成功时的回调
* @param reject 失败时的回调
*/
private resolvePromise(promise2: MyPromise<V, R>, x: any, resolve: IStatusFunc<V>, reject: IStatusFunc<R>) {
// 禁止循环引用
if (x === promise2) {
throw new Error('Chaining cycle detected for promise');
}
// 是否已经调用过的标记
let called: boolean = false;
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
try {
// 如果x.then依旧是function,代表x是promise对象
if (typeof x.then === 'function') {
// x.then继续执行,第一个参数是this,后面依次是成功回调和失败的回调
x.then.call(x, y => {
// 成功和失败只能调用一个
if (called) return;
called = true;
this.resolvePromise(promise2, y, resolve, reject);
}, err => {
// 成功和失败只能调用一个
if (called) return;
called = true;
reject(err);
});
} else {
resolve(x);
}
} catch(e) {
if (called) return;
called = true;
reject(e);
}
} else {
// 如果是普通类型,直接resolve
resolve(x);
}
}
then方法的实现也比较简单
/**
* then方法
* @param onFullfilled 成功的回调函数
* @param onRejected 失败的回调函数
*/
then(onFullfilled: IStatusFunc<V>, onRejected?: IStatusFunc<R>) {
// 返回Promise的目的是为了链式调用
let promise2 = new MyPromise<V, R>((resolve: IStatusFunc<V>, reject: IStatusFunc<R>) => {
if (this.state === Status.RESOLVED) {
let x = onFullfilled(this.value);
this.resolvePromise(promise2, x, resolve, reject);
}
if (this.state === Status.REJECTED) {
let x = onRejected(this.reason);
this.resolvePromise(promise2, x, resolve, reject);
}
if (this.state === Status.PENDING) {
this.resolvedCallbacks.push(() => {
let x = onFullfilled(this.value);
this.resolvePromise(promise2, x, resolve, reject);
});
this.rejectedCallbacks.push(() => {
let x = onRejected(this.reason);
this.resolvePromise(promise2, x, resolve, reject);
});
}
});
return promise2;
}