Promise A+解析 实现一款完整的Promise

757 阅读8分钟

1.术语

  • 1.promise是一个有then方法的对象或者函数
  • 2.thenable是一个有then方法的对象或者函数
  • 3.value是promise成功态的值,值的类型是任何符合JS规范的类型
  • 4.exception是throw抛出异常的值
  • 5.reason是promise失败态的值

2.Promise A+规范

2.1.Promise States

一个promise必须处于三种状态中之一:

  • 1.pending「等待态」
  • 2.fulfilled「成功态」∏
  • 3.rejected「失败态」

2.1.1.pending

  • 初始化的状态,可以改变
  • 可以转化为成功态,或者失败态

2.1.2.fulfilled

  • 最终态,不可变
  • pending->resolve(value)->fulfilled
  • 必须拥有一个value值

2.1.3.rejected

  • 最终态,不可变
  • pending->reject(reason)->rejected
  • 必须拥有一个reason值

2.1.4.改变状态

pending->resolve->fulfilled 「最终态,一旦成功不能在失败」 pending->reject->rejected 「最终态,一旦失败不能在成功」 pending->throw new Error('')->rejected

2.2.then方法

Promise必须提供一个then方法,用来访问最终的结果,无论是value还是reason

promise.then(onFulfilled,onRejected);

2.2.1.参数要求

  • 如果onFulfilled不是函数,必须忽略它 「值的穿透」
  • 如果onRejected不是函数,必须忽略它 「值的穿透」

2.2.2.onFulfilled

  • 当状态变为fulfilled时,应该调用onFulfilled,参数为value
  • 在状态是fulfilled之前,onFulfilled不被调用
  • onFulfilled不能被多次调用

2.2.3.onRejected

  • 当状态为rejected时,应该嗲用onRejected,参数为reason
  • 在状态是rejected之前,onRejected不被调用
  • onRejected不能被多次调用

2.2.4.onFulfilled和onRejected要异步执行

可以通过宏任务或者微任务实现

  • 宏任务:setTimeout或setImmediate
  • 微任务:MutationObserver或者process.nextTick

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

  • promise状态变为fulfilled后,所有的onFulfilled回调都需要按照then的顺序执行,也就是按照注册顺序执行
  • promise状态变为rejected后,所有的onRejected回调都需要按照then的顺序执行,也就是按照注册的顺序执行

2.2.6.then的返回值

then的返回值应该是一个新的promise

promise2 = promise1.then(onFulfilled, onRejected);
  • onFulfilled或onRejected执行后的结果是x,调用resolvePromise(promise2,x,resolve,reject)
  • 如果 onFulfilled或者 onRejected执行抛出异常,promise2需要reject这个异常
  • 如果onFulfilled不是一个函数,promise2以promise1的value触发fulfilled
  • 如果onRejected不是一个函数,promise2以promise1的reason触发这个rejected

2.2.7.resolvePromise

resolvePromise(promise2,x,resolve,reject);
  • 如果promise2和x相同,需要reject一个类型异常「TypeError 不然会死循环」

  • 如果x是一个promise

    • 如果处于pending态中的x,promise必须继续等待,直到x变为fulfilled/rejected
    • 如果处于fulfilled态中的x,promise则以相同的值完成
    • 如果处于rejected态中的x,promise以同样的理由拒绝
  • 如果x是一个object或着function

      1. let then=x.then
      1. 如果x.then出错,那么promise以同样的理由拒绝
      1. 如果then是一个函数,then.call(x,resolvePromiseFn,rejectPromiseFn)
      • resolvePromiseFn的入参是y,执行resolvePromise(promise2,y,resolve,reject)
      • rejectPromiseFn的入参是r,执行reject(r);
      • 如果resolvePromiseFn和rejectPromiseFn都被调用了,那么第一个调用优先,后面的调用忽略
      1. 如果then执行抛出异常
      • 如果resolvePromiseFn或者rejectPromiseFn已经被调用,那么忽略
      • reject抛出的异常
  • 如果then是一个普通值,直接resolve(x);

3.实现

3.1.定义三种状态

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

3.2.class实现一个Promise

  • 1.new Promise是传递一个构造器「executor」
  • 2.executor默认就要执行,并且参数为resolve,和reject函数
  • 3.resolve函数,用来处理从等待态到成功态,并且给value赋值
  • 4.reject函数,用来处理从等待态到失败态,并且给reason赋值
  • 5.executor执行时,可能出错,出错需要reject异常
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class Promise {
    constructor(executor) {
        //初始化状态
        this.state = PENDING;
        //成功是的值
        this.value = null;
        //失败时的值
        this.reason = null;

        const resolve = (value) => {
            if (this.state === PENDING) { //pending->resolve->fulfilled
                this.state = FULFILLED;
                this.value = value;
            }
        }

        //pending->reject(reason)->rejected
        const reject = (reason) => {
            if (this.state === PENDING) {
                this.state = REJECTED;
                this.reason = reason;
            }
        }
        try { //pending->throw new Error->rejected
            executor(resolve, reject);
        } catch (error) {
            reject(error);
        }
    }
}

3.3.then方法

3.3.1.onFulfilled,onRejected

  • 接受两个参数,onFulfilled,onRejected
  • onFulfilled的不是一个函数时,值要往下传递「值的穿透」
  • onRejected的不是一个函数是,值要往下传递「值的穿透」
then(onFulfilled,onRejected){
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
    onRejected = typeof onRejected === 'function' ? onRejected : e => {
            throw e
        }
}

3.3.2.then的返回值是一个新的promsie

如果then的返回值不是一个新的promise会存在一个问题,就是违背一旦成功或者失败是已经是最终态了,接下来的then没办法处理

then(onFulfilled,onRejected){
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
    onRejected = typeof onRejected === 'function' ? onRejected : e => {
            throw e
        }
    const promise2=new Promise((resolve,reject)=>{})
    return promise2;    
}

3.3.3.处理不同的状态

  • FULFILLED:立即成功时的处理
  • REJECTED:立即失败时的处理
then(onFulfilled,onRejected){
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
    onRejected = typeof onRejected === 'function' ? onRejected : e => {
            throw e
        }
    const promise2=new Promise((resolve,reject)=>{
        switch (this.state) {
            case FULFILLED:
                onFulfilled(this.value);
                break;
            case REJECTED:
                onRejected(this.reason);
                break;
        }
    })
    return promise2;    
}

3.3.4.等待态的处理

上面的逻辑我们已经看到,then函数被调用的瞬间就会执行,但是这样对应一个异步来说,它的状态不会立马变成fulfilled或者rejected,该怎么办呢?这时候状态实质时处于等待态的,所有这个时候我们需要一个状态的监听机制,当状态变为fulfilled或者rejected后,再去执行callback,这个我们采用发布订阅模式

  • 1.创建一个_fulfilled_callback_list用来存放成功的回调
  • 2.创建一个_rejected_callback_list用来存放失败的回调
  • 3.当pending->resolve->fulfilled时,循环执行订阅的
  • 4.当pending->reject->rejected时,循环执行订阅的
const promise2=new Promise((resolve,reject)=>{
    switch (this.state) {
        case FULFILLED:
            onFulfilled(this.value);
            break;
        case REJECTED:
            onRejected(this.reason);
            break;
        case PENDING:
            this._fulfilled_callback_list.push(
            () => {
                onFulfilled(this.value);
            })
            this._rejected_callback_list.push(
            () => {
                ronRejected(this.reason);
        })
    }
})
//存储所以等待态中的fulfilled回调
this._fulfilled_callback_list = [];
//存放所用等待态的onRejected
this._rejected_callback_list = [];
const resolve = (value) => {
    //may transition to either the fulfilled or
rejected state.
    if (this.state === PENDING) { //
pending->resolve->fulfilled
        this.value = value;
        this.state = FULFILLED;
        this._fulfilled_callback_list.forEach
(cb => cb());
    }
}
//pending->reject(reason)->rejected
const reject = (reason) => {
    //may transition to either the fulfilled or
rejected state.
    if (this.state === PENDING) {
        this.reason = reason;
        this.state = REJECTED;
        this._rejected_callback_list.forEach(cb
=> cb());
    }
}

3.3.5.promise2

  • 1.onFulfilled,onRejected执行可能抛错,promise2必须拒绝执行
  • 2.onFulfilled,onRejected执行的返回值,可能任意类型「promise...」,需要resolvePromise这个值,处理不同情况
  • 3.resolvePromise(promise2,x,resolve,reject)中的promise2可能不存在,需要异步处理「这里采用setTimeout实现」
//A promise must provide a then method to access its current or eventual value or reason
then(onFulfilled, onRejected) {
    //If onFulfilled is not a function, it must be ignored.
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
    //If onRejected is not a function, it must be ignored.
    onRejected = typeof onRejected === 'function' ? onRejected : e => {
        throw e
    }
    const promise2 = new Promise((resolve, reject) => {
        let realOnFulfilled = () => {
            setTimeout(() => { //onFulfilled or onRejected must not be called until the execution 
context stack contains only platform code
                try {
                    //If either onFulfilled or onRejected returns a value x, run the Promise 
Resolution Procedure [[Resolve]](promise2, x)
                    const x = onFulfilled(this.value);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (error) {
                    reject(error);
                }
            }, 0);
        }
        let realOnRejected = () => {
            setTimeout(() => { //onFulfilled or onRejected must not be called until the execution 
context stack contains only platform code
                try {
                    const x = onRejected(this.reason);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (error) {
                    reject(error);
                }
            }, 0);
        }
        switch (this.state) {
            case FULFILLED:
                realOnFulfilled();
                break;
            case REJECTED:
                realOnRejected();
                break;
            case PENDING:
                this._fulfilled_callback_list.push(() => {
                    realOnFulfilled();
                })
                this._rejected_callback_list.push(() => {
                    realOnRejected();
                })
                break;
        }
    });
    return promise2;
}

3.3.6.resolvePromise

  • 1.x和promise2相同,死循环
  • 2.x是一个Promise 「这里是自己的这个promise」
  • 3.x是对象或者函数
    • 3.1.x有then方法,说明是一个thenable
    • 3.2.x没有then,说明一个普通对象或者null
  • 4.x是一个普通值
function resolvePromise(promise2, x, resolve, reject) {
    //If promise and x refer to the same object, reject promise with a TypeError as the reason.
    if (promise2 === x) {
        reject(new TypeError('死循环'))
    } else if (x instanceof Promise) { //If x is a promise, adopt its state
        x.then(y => {
            resolvePromise(promise2, y, resolve, reject);
        }, reject);
    } else if ((typeof x === 'object' && x !== null) || (typeof x === 'function')) { //Otherwise, if x is an object or function
        let called = false;
        try {
            //Let then be x.then
            let then = x.then;
            if (typeof then === 'function') { //If then is a function
                //call it with x as this
                then.call(x, y => { //first argument resolvePromise
                    if (called) return;
                    called = true;
                    //If/when resolvePromise is called with a value y, run [[Resolve]](promise, y)
                    resolvePromise(promise2, y, resolve, reject);
                }, r => { //second argument rejectPromise
                    if (called) return;
                    called = true;
                    reject(r); //If/when rejectPromise is called with a reason r, reject promise with r
                })
            } else { //If then is not a function, fulfill promise with x.
                resolve(x);
            }
        } catch (error) { //If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason
            if (called) return;
            called = true;
            reject(error);
        }

    } else { //If x is not an object or function, fulfill promise with x
        resolve(x);
    }
}

3.3.7.测试实现的promise

到这里,其实我们已经按照Promise A+规范实现了一个Promise,接下来我们测试一下自己写的promise

npm install promises-aplus-tests -g
Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve;
        dfd.reject = reject;
    })
    return dfd;
}
promises-aplus-tests promise.js

3.4.catch实现

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

3.5.Promise.resolve实现

  • 1.resolve方法是一个静态方法
  • 2.resolve的参数可以是任何类型
  • 3.resolve的返回结果是一个promise
static resolve(value) {
    if (value instanceof Promise) return value;
    return new Promise(resolve => {
        resolve(value);
    })
}

3.6.Promise.reject实现

  • 1.reject方法是一个静态方法
  • 2.reject的参数可以是任何类型
  • 3.reject的返回结果是一个promise
static reject(reason) {
    return new Promise((_, reject) => {
        reject(reason);
    })
}

3.7.Promise.all实现

可以解决异步并发问题,并且返回的结果按照调用的顺序进行存储。全部成功后才成功否则执行失败逻辑

  • 1.all方法是一个静态方法
  • 2.all的参数可以是任何类型的数组
  • 3.all的返回结果是一个promise
static all(promiseList) {
    return new Promise((resolve, reject) => {
        let len = promiseList.length,
            timers = 0;
        result = [];
        const resolveResult = (value, index) => {
            result[index] = value;
            if (++timers === len) {
                resolve(result);
            }
        }
        for (let i = 0; i < len; i++) {
            const value = promiseList[i];
            if (isPromise(value)) {
                value.then(x => {
                    resolveResult(x, i);
                }, reject)
            } else {
                resolveResult(value, i);
            }
        }
    })
}

3.8.Promise.race实现

赛跑问题处理,无论成功还是失败,返回第一个返回的值

  • 1.race方法是一个静态方法
  • 2.race的参数可以是任何类型的数组
  • 3.race的返回结果是一个promise
static race(promiseList) {
    return new Promise((resolve, reject) => {
        let len = promiseList.length;
        if (len === 0) resolve();
        else {
            for (let i = 0; i < len; i++) {
                Promise.resolve(promiseList[i]).then
(value => {
                    resolve(value)
                }, reason => {
                    reject(reason);
                })
            }
        }
    })
}

3.9.finally实现

  • 1.finally回调没有参数
  • 2.finally返回的是一个promise
  • 3.finally不管成功还是失败都会执行
  • 4.finally成功的值不会作为下一个then的值
  • 5.finally失败的值会终断链,他失败的值会作为下一次then的值
finally(callback) { //callback没有参数
    return this.then(data => { //不管成功还是失败
callback都会执行
        //callback执行,可能是一个promise需要等待他执行完//成功需要将then的值传递下去,而不是callback的值
        return Promise.resolve(callback()).then(_ 
=> data);
    }, error => {
        //需要调用resolve因为reject不会等待,然后在then中
将错误值抛出去
        return Promise.resolve(callback()).then(_ 
=> {
            throw error
        });
    });
}