学习笔记之手写实现Promise

287 阅读11分钟

前言

本着学习为目的,整理记录自己学到的东西,如有纰漏,请各位大佬指出,不胜感激!

promiseA+规范

要手写实现一个promise,首先我们要了解promiseA+规范,这里贴出地址:promisesaplus.com 有需要的自取

一. promise states (promise有哪些状态)

  1. pending

    1.1 初始状态,可以改变
    1.2 一个promise在resolve或者reject之前都处于这个状态
    1.3 可以通过resolve 变成 fulfilled状态
    1.4 可以通过reject变成rejected状态

  2. fulfilled

    2.1 最终状态,不可改变
    2.2 一个promise被resolve之后的状态
    2.3 必须拥有一个value值

  3. rejected

    3.1 最终状态,不可改变
    3.2 一个promise被rejected之后的状态
    3.3 必须拥有一个reason值

二. then

promise应该提供一个then方法,用来访问最终的结果,无论是value还是reason

promise.then(onFulfilled, onRejected)
  1. 参数要求

    1.1 onFulfilled 必须是函数类型, 如果不是函数, 应该被忽略.
    1.2 onRejected 必须是函数类型, 如果不是函数, 应该被忽略.

  2. onFulfilled 特性

    2.1 在promise变成 fulfilled 时,应该调用 onFulfilled, 参数是value
    2.2 在promise变成 fulfilled 之前, 不应该被调用.
    2.3 只能被调用一次(所以在实现的时候需要一个变量来限制执行次数)

  3. onRejected 特性

    3.1 在promise变成 rejected 时,应该调用 onRejected, 参数是reason
    3.2 在promise变成 rejected 之前, 不应该被调用.
    3.3 只能被调用一次(所以在实现的时候需要一个变量来限制执行次数)

  4. onFulfilled 和 onRejected 应该是微任务

    这里用queueMicrotask来实现微任务的调用.

  5. then方法可以被调用多次

    5.1 promise状态变成 fulfilled 后,所有的 onFulfilled 回调都需要按照then的顺序执行, 也就是按照注册顺序执行(所以在实现的时候需要一个数组来存放多个onFulfilled的回调)
    5.2 promise状态变成 rejected 后,所有的 onRejected 回调都需要按照then的顺序执行, 也就是按照注册顺序执行(所以在实现的时候需要一个数组来存放多个onRejected的回调)

  6. 返回值

    then 应该返回一个promise

    promise2 = promise1.then(onFulfilled, onRejected);
    

    6.1 onFulfilled 或 onRejected 执行的结果为x, 调用 resolvePromise( 这里大家可能难以理解, 可以先保留疑问, 下面详细讲一下resolvePromise是什么东西 )
    6.2 如果 onFulfilled 或者 onRejected 执行时抛出异常e, promise2需要被reject
    6.3 如果 onFulfilled 不是一个函数, promise2 以promise1的value 触发fulfilled
    6.4 如果 onRejected 不是一个函数, promise2 以promise1的reason 触发rejected

  7. resolvePromise

    resolvePromise(promise2, x, resolve, reject)
    

    7.1 如果 promise2 和 x 相等,那么 reject TypeError
    7.2 如果 x 是一个 promsie
    如果x是pending态,那么promise必须要在pending,直到 x 变成 fulfilled or rejected.
    如果 x 被 fulfilled, fulfill promise with the same value.
    如果 x 被 rejected, reject promise with the same reason.
    7.3 如果 x 是一个 object 或者 是一个 function
    let then = x.then.
    如果 x.then 这步出错,那么 reject promise with e as the reason.
    如果 then 是一个函数,then.call(x, resolvePromiseFn, rejectPromise)
    resolvePromiseFn 的 入参是 y, 执行 resolvePromise(promise2, y, resolve, reject);
    rejectPromise 的 入参是 r, reject promise with r.
    如果 resolvePromise 和 rejectPromise 都调用了,那么第一个调用优先,后面的调用忽略。
    如果调用then抛出异常e
    如果 resolvePromise 或 rejectPromise 已经被调用,那么忽略
    则,reject promise with e as the reason
    如果 then 不是一个function. fulfill promise with x.

实现promise

了解了promiseA+规范之后,我们就可以依照规范,手动编写实现一个promise了, 实现方式可以用构造函数和Class两种方式,这里我们采用Class来实现

1. 定义Class

class MPromise = {
    constructor() {
    }
}

2. 定义3种初始状态

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

3. 设置初始状态

class MPromise = {
    constructor() {
        this.status = PENDING;
        this.value = null;
        this.reason = null;
    }
}

4. resolve和reject方法

class MPromise {
    constructor() {
        // 初始状态为pending
        this.status = PENDING;
        this.value = null;
        this.reason = null;
    }

    resolve(value) {
        if (this.status === PENDING) {
            this.value = value;
            this.status = FULFILLED;
        }
    }

    reject(reason) {
        if (this.status === PENDING) {
            this.reason = reason;
            this.status = REJECTED;
        }
    }
}

5. 添加promise入参

5.1 入参是一个函数, 函数接收resolve和reject两个参数.
5.2 注意在初始化promise的时候, 就要执行这个函数, 并且有任何报错都要通过reject抛出去

class MPromise {
    constructor(fn) {
        // 初始状态为pending
        this.status = PENDING;
        this.value = null;
        this.reason = null;
        try{
            // bind在这里是为了绑定this为MPromise,避免this指向发生改变
            fn(this.resolve.bind(this),this.reject.bind(this))
        }catch(e){
            this.reject(e)
        }
    }

    resolve(value) {
        if (this.status === PENDING) {
            this.value = value;
            this.status = FULFILLED;
        }
    }

    reject(reason) {
        if (this.status === PENDING) {
            this.reason = reason;
            this.status = REJECTED;
        }
    }
}

6. then方法

//声明一个通用函数,用来判断是否为function类型
isFunction(param) {
    return typeOf param === 'function';
}
// then接收两个参数, onFulfilled 和 onRejected
then(onFulfilled,onRejected) {
    // 对接收的参数类型进行判断,如果不是函数,则忽略
    const realOnFulfilled = isFunction(onFulfilled) ? onFulfilled : value => value;
    const realOnRejected = isFunction(onRejected) ? onRejected : reason => reason;
    // then的返回值是一个新的promise
    const promise2 = new MPromise((resolve,reject)=>{
    // 根据当前promise的状态,调用不同的函数
    switch (this.status) {
        case FULFILLED : {
            realOnFulfilled()
            break;
        }
        case REJECTED : {
            realOnRejected()
            break;
        }
        case PENDING : {
            // 待下文补充
            ...
            break;
        }
    }
})
return promise2;
}

上面这种写法在then函数调用的时候就会立即执行,而如果此时promise的status还没变成fulfilled或者rejected,仍然是pending状态的话,就没办法执行callback

所以我们需要一个监听机制,当status变成fulfilled或者rejected后,才执行callback

首先我们要拿到所有的callback,然后在某个时机去执行它们,所以我们需要新建两个数组,用于存放成功和失败的callback,当调用then方法的时候,如果promise的状态还是pending,则将callback存入数组中,等到状态改变后再执行

FULFILLED_CALLBACK_LIST = [];
REJECTED_CALLBACK_LIST = [];

then(onFulfilled, onRejected) {
        // 如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值 
        const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
            return value
        }
        // 如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因。
        const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => {
            throw reason;
        };
        const promise2 = new MPromise((resolve, reject) => {
            switch (this.status) {
                case FULFILLED: {
                    realOnFulfilled()
                    break;
                }
                case REJECTED: {
                    realOnRejected()
                    break;
                }
                case PENDING: {
                // 当调用then方法的时候,如果promise的状态还是pending,则将callback存入数组中,等到状态改变后再执行
                    this.FULFILLED_CALLBACK_LIST.push(realOnFulfilled)
                    this.REJECTED_CALLBACK_LIST.push(realOnRejected)
                }
            }
        })
        return promise2

}

在status发生改变后,就执行所有的回调,这里可以使用es6的getter和setter来处理

// 声明一个_status避免触发getter的时候陷入死循环
_status = PENDING;

get status() {
    return this._status;
}
// 触发setter的时候,更新_status,并根据newStatus的状态,遍历并执行对应的回调函数
set status(newStatus) {
    this._status = newStatus
    switch(newStatus) {
        case FULFILLED: {
            this.FULFILLED_CALLBACK_LIST.forEach(callback=>{
                callback(this.value)
            })
            break;
        }
        case REJECTED: {
            this.REJECTED_CALLBACK_LIST.forEach(callback => {
                callback(this.reason);
            });
            break;
        }
    }
}

7. then的返回值

上面提到then的返回值是一个新的promise,那么这个promise的value和reason是什么呢

根据promiseA+规范我们知道,如果onFulfilled或者onRejected抛出一个异常e,则 promise2必须拒绝执行,并返回拒因e。所以我们需要使用try catch手动捕获错误,并reject出来

then(onFulfilled,onRejected) {
    const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
        return value
    }
    const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => {
        throw reason;
    };
    const promise2 = new Promise((resolve,reject)=>{
        const fulfilledMicrotasck = () => {
            try {
                realOnFulfilled(this.value);
            }catch(e) {
                reject(e)
            }
        }
        const rejectedMicrotask = () => {
            try {
                realOnRejected(this.reason);
            } catch (e) {
                reject(e);
            }
        }
        switch (this.status) {
            case FULFILLED: {
                fulfilledMicrotask()
                break;
            }
            case REJECTED: {
                rejectedMicrotask()
                break;
            }
            case PENDING: {
                this.FULFILLED_CALLBACK_LIST.push(fulfilledMicrotask)
                this.REJECTED_CALLBACK_LIST.push(rejectedMicrotask)
            }
        }
    })
    return promise2
}

8. resolvePromise方法

我们已经处理了then接收到onFulfilled 和 onRejected参数如果不是函数或者如果抛出异常的情况

那么接下来就要处理如果 onFulfilled 和 onRejected 是一个函数,并且正常执行了的情况了,我们设定一个resolvePromise方法来处理这种情况

如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行resolvePromise方法

    then(onFulfilled, onRejected) {
        const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
            return value
        }
        const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => {
            throw reason;
        };
        const promise2 = new MPromise((resolve, reject) => {
            const fulfilledMicrotask = () => {
                try {
                    const x = realOnFulfilled(this.value);
                    this.resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e)
                }
            };
            const rejectedMicrotask = () => {
                try {
                    const x = realOnRejected(this.reason);
                    this.resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            }

            switch (this.status) {
                case FULFILLED: {
                    fulfilledMicrotask()
                    break;
                }
                case REJECTED: {
                    rejectedMicrotask()
                    break;
                }
                case PENDING: {
                    this.FULFILLED_CALLBACK_LIST.push(fulfilledMicrotask)
                    this.REJECTED_CALLBACK_LIST.push(rejectedMicrotask)
                }
            }
        })
        return promise2
    }
    // 这个函数是根据promiseA+规范来处理一些异常情况的,主要用来解析promise2和x
   
    resolvePromise(promise2, x, resolve, reject) {
        // 如果 newPromise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 newPromise
        // 这是为了防止死循环
        if (promise2 === x) {
            return reject(new TypeError('The promise and the return value are the same'));
        }

        if (x instanceof MPromise) {
            // 如果 x 为 Promise ,则使 newPromise 接受 x 的状态
            // 也就是继续执行x,如果执行的时候拿到一个y,还要继续解析y
            queueMicrotask(() => {
                x.then((y) => {
                    this.resolvePromise(promise2, y, resolve, reject);
                }, reject);
            })
        } else if (typeof x === 'object' || this.isFunction(x)) {
            // 如果 x 为对象或者函数
            if (x === null) {
                // null也会被判断为对象
                return resolve(x);
            }

            let then = null;

            try {
                // 把 x.then 赋值给 then 
                then = x.then;
            } catch (error) {
                // 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
                return reject(error);
            }

            // 如果 then 是函数
            if (this.isFunction(then)) {
                let called = false;
                // 将 x 作为函数的作用域 this 调用
                // 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
                try {
                    then.call(
                        x,
                        // 如果 resolvePromise 以值 y 为参数被调用,则运行 resolvePromise
                        (y) => {
                            // 需要有一个变量called来保证只调用一次.
                            if (called) return;
                            called = true;
                            this.resolvePromise(promise2, y, resolve, reject);
                        },
                        // 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
                        (r) => {
                            if (called) return;
                            called = true;
                            reject(r);
                        });
                } catch (error) {
                    // 如果调用 then 方法抛出了异常 e:
                    if (called) return;

                    // 否则以 e 为据因拒绝 promise
                    reject(error);
                }
            } else {
                // 如果 then 不是函数,以 x 为参数执行 promise
                resolve(x);
            }
        } else {
            // 如果 x 不为对象或者函数,以 x 为参数执行 promise
            resolve(x);
        }
    } 

9. 模拟微任务

onFulfilled 和 onRejected 是微任务就, 咱们可以用queueMicrotask包裹执行函数

const fulfilledMicrotask = () => {
    queueMicrotask(() => {
        try {
            const x = realOnFulfilled(this.value);
            this.resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
            reject(e)
        }
    })
};
const rejectedMicrotask = () => {
    queueMicrotask(() => {
        try {
            const x = realOnRejected(this.reason);
            this.resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
            reject(e);
        }
    })
}

10. catch方法

catch (onRejected) {
    return this.then(null, onRejected);
}

11. promise.resolve方法 和 promise.reject方法

promise.resolve

将现有对象转为Promise对象,如果 Promise.resolve 方法的参数,不是具有 then 方法的对象(又称 thenable 对象),则返回一个新的 Promise 对象,且它的状态为fulfilled。

注意这是一个静态方法, 因为咱们是通过Promise.resolve调用的, 而不是通过实例去调用的.

static resolve(value) {
    if (value instanceof MPromise) {
        return value;
    }

    return new MPromise((resolve) => {
        resolve(value);
    });
}

promise.reject

返回一个新的Promise实例,该实例的状态为rejected。Promise.reject方法的参数reason,会被传递给实例的回调函数。

static reject(reason) {
    return new MPromise((resolve, reject) => {
        reject(reason);
    });
}

12. promise.race() 方法

const p = Promise.race([p1, p2, p3]);

该方法是将多个 Promise 实例,包装成一个新的 Promise 实例。只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

static race(promiseList) {
    return new MPromise((resolve, reject) => {
        const length = promiseList.length;

        if (length === 0) {
            return resolve();
        } else {
            for (let i = 0; i < length; i++) {
                MPromise.resolve(promiseList[i]).then(
                    (value) => {
                        return resolve(value);
                    },
                    (reason) => {
                        return reject(reason);
                    });
            }
        }
    });

}

13. promise.all() 方法

const p = Promise.all([p1, p2, p3]);

当所有promise都执行完了,返回执行结果的集合,如果有一个失败,则返回失败信息

static all (promiseList) {
        return new MPromise((resolve, reject) => {
          const length = promiseList.length
          let result = []
          let count = 0
          if (length === 0) {
            return resolve()
          } else {
            for (let i = 0; i < length; i++) {
              MPromise.resolve(promiseList[i]).then(
                (value) => {
                  count++;
                  result[i] = value
                  if (count === length) {
                    return resolve(result)
                  } 
                },
                (reason) => {
                  return reject(reason)
                }
              )
            } 
          }
        })
  }

完整代码

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MPromise {

    FULFILLED_CALLBACK_LIST = [];
    REJECTED_CALLBACK_LIST = [];
    _status = PENDING;
    constructor(fn) {
        // 初始状态为pending
        this.status = PENDING;
        this.value = null;
        this.reason = null;
        try {
            fn(this.resolve.bind(this), this.reject.bind(this));
        } catch (e) {
            this.reject(e);
        }
    }

    get status() {
        return this._status;
    }

    set status(newStatus) {
        this._status = newStatus;
        switch (newStatus) {
            case FULFILLED: {
                this.FULFILLED_CALLBACK_LIST.forEach(callback => {
                    callback(this.value);
                });
                break;
            }
            case REJECTED: {
                this.REJECTED_CALLBACK_LIST.forEach(callback => {
                    callback(this.reason);
                });
                break;
            }
        }
    }

    resolve(value) {
        if (this.status === PENDING) {
            this.value = value;
            this.status = FULFILLED;
        }
    }

    reject(reason) {
        if (this.status === PENDING) {
            this.reason = reason;
            this.status = REJECTED;
        }
    }

    then(onFulfilled, onRejected) {
        const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
            return value
        }
        const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => {
            throw reason;
        };
        const promise2 = new MPromise((resolve, reject) => {
            const fulfilledMicrotask = () => {
                queueMicrotask(() => {
                    try {
                        const x = realOnFulfilled(this.value);
                        this.resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e)
                    }
                })
            };
            const rejectedMicrotask = () => {
                queueMicrotask(() => {
                    try {
                        const x = realOnRejected(this.reason);
                        this.resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                })
            }

            switch (this.status) {
                case FULFILLED: {
                    fulfilledMicrotask()
                    break;
                }
                case REJECTED: {
                    rejectedMicrotask()
                    break;
                }
                case PENDING: {
                    this.FULFILLED_CALLBACK_LIST.push(fulfilledMicrotask)
                    this.REJECTED_CALLBACK_LIST.push(rejectedMicrotask)
                }
            }
        })
        return promise2

    }

    catch (onRejected) {
        return this.then(null, onRejected);
    }

    isFunction(param) {
        return typeof param === 'function';
    }

    resolvePromise(promise2, x, resolve, reject) {
        // 如果 newPromise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 newPromise
        // 这是为了防止死循环
        if (promise2 === x) {
            return reject(new TypeError('The promise and the return value are the same'));
        }

        if (x instanceof MPromise) {
            // 如果 x 为 Promise ,则使 newPromise 接受 x 的状态
            // 也就是继续执行x,如果执行的时候拿到一个y,还要继续解析y
            queueMicrotask(() => {
                x.then((y) => {
                    this.resolvePromise(promise2, y, resolve, reject);
                }, reject);
            })
        } else if (typeof x === 'object' || this.isFunction(x)) {
            // 如果 x 为对象或者函数
            if (x === null) {
                // null也会被判断为对象
                return resolve(x);
            }

            let then = null;

            try {
                // 把 x.then 赋值给 then 
                then = x.then;
            } catch (error) {
                // 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
                return reject(error);
            }

            // 如果 then 是函数
            if (this.isFunction(then)) {
                let called = false;
                // 将 x 作为函数的作用域 this 调用
                // 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
                try {
                    then.call(
                        x,
                        // 如果 resolvePromise 以值 y 为参数被调用,则运行 resolvePromise
                        (y) => {
                            // 需要有一个变量called来保证只调用一次.
                            if (called) return;
                            called = true;
                            this.resolvePromise(promise2, y, resolve, reject);
                        },
                        // 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
                        (r) => {
                            if (called) return;
                            called = true;
                            reject(r);
                        });
                } catch (error) {
                    // 如果调用 then 方法抛出了异常 e:
                    if (called) return;

                    // 否则以 e 为据因拒绝 promise
                    reject(error);
                }
            } else {
                // 如果 then 不是函数,以 x 为参数执行 promise
                resolve(x);
            }
        } else {
            // 如果 x 不为对象或者函数,以 x 为参数执行 promise
            resolve(x);
        }
    }

    static resolve(value) {
        if (value instanceof MPromise) {
            return value;
        }

        return new MPromise((resolve) => {
            resolve(value);
        });
    }

    static reject(reason) {
        return new MPromise((resolve, reject) => {
            reject(reason);
        });
    }

    static race(promiseList) {
        return new MPromise((resolve, reject) => {
            const length = promiseList.length;

            if (length === 0) {
                return resolve();
            } else {
                for (let i = 0; i < length; i++) {
                    MPromise.resolve(promiseList[i]).then(
                        (value) => {
                            return resolve(value);
                        },
                        (reason) => {
                            return reject(reason);
                        });
                }
            }
        }); 
    }
    static all(promiseList) {
        return new MPromise((resolve, reject) => {
          const length = promiseList.length
          let result = []
          let count = 0
          if (length === 0) {
            return resolve()
          } else {
            for (let i = 0; i < length; i++) {
              MPromise.resolve(promiseList[i]).then(
                (value) => {
                  count++;
                  result[i] = value
                  if (count === length) {
                    return resolve(result)
                  } 
                },
                (reason) => {
                  return reject(reason)
                }
              )
            } 
          }
        })
  }
}