手写PromiseA+规范/Promise

·  阅读 157

最近学习了PromiseA+规范和手写Promise,记录一下并分享给有需要的各位兄弟姐妹。接下来我们一起来学习一下promiseA+规范。

PromiseA+规范

了解PromiseA+规范之前我们得知道Promise到底是什么?

  1. promise是什么?
  • promise 是异步编程的一种思想,可以让异步的任务状态能够更清晰的管理,以及异步任务执行有了清晰的顺序,并且通过.then方法获取resolve或者reject的状态。
  • promise 是一个有then方法的对象或函数,行为遵循promiseA+规范

接下来我们来了解一下PromiseA+规范

术语

  1. promise 是一个有then方法的对象或者是函数,行为遵循本规范
  2. thenable 是一个有then方法的对象或者是函数
  3. value 是promise状态成功时的值,也就是resolve的参数, 包括各种数据类型, 也包括undefined/thenable或者是 promise
  4. reason 是promise状态失败时的值, 也就是reject的参数, 表示拒绝的原因
  5. exception 是一个使用throw抛出的异常值

规范

接下来分几部分来看PromiseA+规范.

Promise状态

  1. pending 初始态,一个promise在resolve或者reject之前都是这个状态
  2. fulfilled 最终态,一个promise在resolve之后的状态(不可变),必须有一个value值
  3. rejected 最终态,一个promise在reject之后的状态(不可变),必须有一个reason值

Tip:即 pending -> resolve(value) -> fulfilled pending -> reject(reason) -> rejected

then

  1. promise一个提供一个then方法来访问最终的结果,无论是当前值(value)或据因(reason)
  2. promise.then(onFulfilled, onRejected)
  •  onFulfilled和onRejected必须是函数类型,如果不是则被忽略
    复制代码
  •  在promise变成 fulfilled(rejected) 时,应该调用 onFulfilled(onRejected), 参数是value(reason)
    复制代码
  •  promise状态变成最终态之前onFulfilled, onRejected都不应该被调用
    复制代码
  •  onFulfilled, onRejected都只能被调用一次
    复制代码
  1. then方法可以被调用多次
  •  promise状态变成 fulfilled 后,所有的 onFulfilled 回调都需要按照then的顺序执行, 也就是按照注册顺序执行(所以在实现的时候需要一个数组来存放多个onFulfilled的回调)
    复制代码
  •  promise状态变成 rejected 后,所有的 onRejected 回调都需要按照then的顺序执行, 也就是按照注册顺序执行(所以在实现的时候需要一个数组来存放多个onRejected的回调)
    复制代码
  1. 返回值

then 应该返回一个promise

   promise2 = promise1.then(onFulfilled, onRejected);
复制代码
  1. onFulfilled 或 onRejected 执行的结果为x, 调用 resolvePromise(这个解决过程需要用到下方的resolvePromise来写)
    复制代码
  2. 如果 onFulfilled 或者 onRejected 执行时抛出异常e, promise2需要被reject
    复制代码
  3. 如果 onFulfilled 不是一个函数, promise2 以promise1的value 触发fulfilled
    复制代码
  4. 如果 onRejected 不是一个函数, promise2 以promise1的reason 触发rejected
    复制代码

resolvePromise

   resolvePromise(promise2, x, resolve, reject)
复制代码
  1. 如果 promise2 和 x 相等,那么 reject TypeError
  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.
    复制代码
  1. 如果 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

这里用es6中的class来实现Promise

  1. 定义一个class来实现promise
class MPromise {
    constructor() {

    }
}
复制代码
  1. 定义三种状态类型
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
复制代码
  1. 初始化状态、value值、reason值
class MPromise {
    constructor() {
        // 初始状态为pending
        this.status = PENDING;
        this.value = null;
        this.reason = null;
    }
}
复制代码
  1. resolve 和 reject 方法

根据刚才的规范, 这两个方法是要更改promise状态的, 从pending改到fulfilled/rejected.当状态变成fulfilled或rejected就不可再更改

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

    reject(reason) {
        if (this.status === PENDING) {
            this.reason = reason;
            this.status = REJECTED;
        }
    }
}
复制代码
  1. 在promise中我们还需要一个入参,入参是一个函数,该函数接收resolve和reject两个参数,注意在初始化promise的时候, 就要执行这个函数, 并且有任何报错都要通过reject抛出去
class MPromise {
    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);
        }
    }
复制代码
  1. 接下来是关键的then方法(如有错误请大佬指点)
    1. then接收两个参数, onFulfilled 和 onRejected
    then(onFulfilled, onRejected) {}
复制代码
    1. 检查并处理参数, 之前提到的onFulfilled 和 onRejected如果不是function, 就忽略(指的是原样返回value或者reason).
    isFunction(param) {
        return typeof param === 'function';
    }
    //判断是否是function
    then(onFulfilled, onRejected) {
        const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => {//值的穿透
            return value
        }
        const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => {
            throw reason;
        };
    }
复制代码
  • 3.根据当前promise的状态, 调用不同的函数。 但是需要注意的是:在then函数被调用的瞬间就会执行. 那这时候如果status还没变成fulfilled或者rejected. 所以我们需要一个状态的监听机制, 当状态变成fulfilled或者rejected后, 再去执行callback.
class MPromise {
    constructor() {
        FULFILLED_CALLBACK_LIST = [];
        REJECTED_CALLBACK_LIST = [];
        _status = PENDING;
        ...[省略之前代码]
        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) => {
            switch (this.status) {
                case FULFILLED: {
                    realOnFulfilled()
                    break;
                }
                case REJECTED: {
                    realOnRejected()
                    break;
                }
                case PENDING: {
                    this.FULFILLED_CALLBACK_LIST.push(realOnFulfilled)
                    this.REJECTED_CALLBACK_LIST.push(realOnRejected)
                }
            }
        })
        return promise2
        }
    }
}
复制代码
  • 4.在status发生变化的时候, 需要执行所有的回调. 这里咱们用一下es6的getter和setter.

        _status = PENDING; //设置一个_state存储当前状态,防止陷入死循环
    
        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;
                }
            }
        }
    复制代码
  • 5.then的返回值

5.1. then的返回值是一个Promise,所以我们需要返回promise。如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e。这样的话, 我们就需要手动catch代码,遇到报错就reject。

then(onFulfilled, onRejected) {
        const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
            return value
        }
        const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => {
            throw reason;
        };
        //包装两个tryCatch函数
        const promise2 = new MPromise((resolve, reject) => {
            const fulfilledMicrotask = () => {   
                try {
                    realOnFulfilled(this.value);
                } catch (e) {
                    reject(e)
                }
            };
            const rejectedMicrotask = () => {
                try {
                    realOnRejected(this.reason);
                } catch (e) {
                    reject(e);
                }
            }
            //根据status返回一个promise
            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
    }
复制代码

5.2 如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值

5.3 如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因。 需要注意的是,如果promise1的onRejected执行成功了,promise2应该被resolve.

    const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
        return value
    }
    const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => {
        throw reason;
    };
复制代码

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

  resolvePromise(promise2, x, resolve, reject)
复制代码

resolvePromise

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(() => {
        //onFulfilled 和 onRejected 是微任务,咱们可以用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);
    }
}
复制代码

测试一下

const test = new MPromise((resolve, reject) => {
    setTimeout(() => {
        reject(111);
    }, 1000);
}).then((value) => {
    console.log('then');
}).catch((reason) => {
    console.log('catch');    //catch
})
复制代码
分类:
前端