实现一个Promise


基本框架

首先打开Promises/A+规范,根据如下要求构建代码框架。

  • “promise” is an object or function with a then method whose behavior conforms to this specification.
  • A promise must provide a then method to access its current or eventual value or reason.
class myPromise {
    constructor(){

    }

    resolve(){

    }

    reject(){

    }

    then(){

    }
}

State

根据promise的使用方法可以知道promise共有三种状态,相关规范如下:

  • A promise must be in one of three states: pending, fulfilled, or rejected.

在class最上方加入相关代码

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

初始化

一个promise对象在constructor调用时进行初始化,需要初始化其state和result

onFulfilledCallbacks/onRejectedCallbacks用于保存then方法传入的参数

this.PromiseState = myPromise.PENDING; 
this.PromiseResult = null;
this.onFulfilledCallbacks = []; 
this.onRejectedCallbacks = []; 

resolve 和 reject

在给constuctor传入的函数内调用resolve和reject改变promise状态,在resolve/reject执行时还会执行then方法传入的相关参数。

  • When pending, a promise: may transition to either the fulfilled or rejected state.
  • When fulfilled, a promise: must not transition to any other state. must have a value, which must not change.
  • When rejected, a promise: must not transition to any other state. must have a reason, which must not change.
resolve(result) { 
    if (this.PromiseState === myPromise.PENDING) {
        setTimeout(() => {
            this.PromiseState = myPromise.FULFILLED;
            this.PromiseResult = result;
            this.onFulfilledCallbacks.forEach(callback=> {
                callback(result)
            })
        });
    }
}

reject(reason) { 
    if (this.PromiseState === myPromise.PENDING) {
        setTimeout(() => {
            this.PromiseState = myPromise.REJECTED;
            this.PromiseResult = reason;
            this.onRejectedCallbacks.forEach(callback=> {
                callback(reason)
            })
        });
    }
}

解释一下resolve/reject内部异步的原因:

Promise/A+有提到 onFulfilled or onRejected must not be called until the execution context stack contains only platform code.

onFulfilled/onRejected作为then传入的参数需要在所有同步代码执行后才能执行,因此异步的resolve/reject可以保证其执行顺序

then

then方法接受两个参数,这两个参数会在promise状态改变后执行,如果参数不是函数,则应该被忽略

  • Both onFulfilled and onRejected are optional arguments
  • If onFulfilled is not a function, it must be ignored
  • If onRejected is not a function, it must be ignored
onFulfilled = typeof onFulfilled === 'function' onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejecte: e => {throw e};

其次我们知道then方法可以被多次调用,也就是说then方法一定会返回一个promise,then方法的代码框架应该如下

  • then may be called multiple times on the same promise.
let promise2 = new myPromise((resolve, reject) => {
    //some code
}
return promise2

then方法返回的promise(下面成为promise2)状态由onFulfilled/onRejected的返回值决定,该函数的返回值由三种情况:

  • 简单值,如果是简单值(number,boolean,普通object等),则promise2的值也为简单值
  • promise对象,如果onFulfilled/onRejected也返回一个promise,则promise2的状态和值将由新返回的promise决定(这个决定的过程成为resolvePromise)
  • thenable对象,如果返回的对象拥有一个then函数,则尝试resolve过程

参考A+相关标准:

  • If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
  • If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.

在then内加入如下代码

if (this.PromiseState === myPromise.FULFILLED) {
    setTimeout(() => {
        try {
            let x = onFulfilled(this.PromiseResult);
            resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
                reject(e); 
        }
    });
} else if (this.PromiseState === myPromise.REJECTED) {
    setTimeout(() => {
        try {
            let x = onRejected(this.PromiseResult);
            //resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
            reject(e)
        }
    });
} else {
    this.onFulfilledCallbacks.push(() => {
        setTimeout(() => {
            try {
                let x = onFulfilled(this.PromiseResult);
                //resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
                reject(e);
            }
        });
    });
    this.onRejectedCallbacks.push(() => {
        setTimeout(() => {
            try {
                let x = onRejected(this.PromiseResult);
                //resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
                reject(e);
            }
        });
    });
}

这里根据promise的三种状态分为三种情况:

  • promise处于pending状态则将两个参数反别放入对应数组等待执行
  • promise处于fulfilled状态,则立刻执行onFulfilled
  • promise处于rejected状态,则执行onRejected

resolvePromise

resolvePromise处理onFulfilled/onRejected返回值的三种情况,如果onFulfilled/onRejected依然返回promise则继续执行resolve过程,也就是说then方法返回的promise状态由onFulfilled/onRejected “最深层” 的promise决定

function resolvePromise(promise2, x, resolve, reject) {
    if (x === promise2) {
        return reject(new TypeError('Chaining cycle detected for promise'));
    }
    // promise
    if (x instanceof myPromise) {
        if (x.PromiseState === myPromise.PENDING) {
            x.then(y => {
                resolvePromise(promise2, y, resolve, reject)
            }, reject);
        } else if (x.PromiseState === myPromise.FULFILLED) {
            resolve(x.PromiseResult);
        } else if (x.PromiseState === myPromise.REJECTED) {
            reject(x.PromiseResult);
        }
    } else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    //thenable
        let then
        try {
            then = x.then;
        } catch (e) {
            return reject(e);
        }

        if (typeof then === 'function') {
            let called = false; 
            try {
                then.call(x, 
                    (a) =>{
                        if(called){
                            return
                        }
                        called = true
                        resolvePromise(promise2, a, resolve, reject)
                    },
                    (b) =>{
                        if(called){
                            return
                        }
                        called = true
                        reject(b)
                    })
            } catch (e) {
                if (called) return;
                called = true;
                reject(e);
            }
        } else {
            resolve(x);
        }
    } else {
    //简单值
        return resolve(x);
    }
}

called的作用:thenable对象并不是promise,但是依然要求其遵守相关规范(状态不可变且回调函数只能执行一次)

完整代码

class myPromise {
    static PENDING = 'pending';
    static FULFILLED = 'fulfilled';
    static REJECTED = 'rejected';

    constructor(func) { 
        this.PromiseState = myPromise.PENDING; 
        this.PromiseResult = null;  
        this.onFulfilledCallbacks = []; 
        this.onRejectedCallbacks = []; 
        try {
            func(this.resolve.bind(this), this.reject.bind(this));
        } catch (error) {
            this.reject(error)
        }
    }

    resolve(result) { 
        if (this.PromiseState === myPromise.PENDING) {
            setTimeout(() => {
                this.PromiseState = myPromise.FULFILLED;
                this.PromiseResult = result;
                this.onFulfilledCallbacks.forEach(callback => {
                    callback(result)
                })
            });
        }
    }

    reject(reason) { 
        if (this.PromiseState === myPromise.PENDING) {
            setTimeout(() => {
                this.PromiseState = myPromise.REJECTED;
                this.PromiseResult = reason;
                this.onRejectedCallbacks.forEach(callback => {
                    callback(reason)
                })
            });
        }
    }

    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
        onRejected = typeof onRejected === 'function' ? onRejected : reason => {
            throw reason;
        };

        let promise2 = new myPromise((resolve, reject) => {
            if (this.PromiseState === myPromise.FULFILLED) {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.PromiseResult);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e); 
                    }
                });
            } else if (this.PromiseState === myPromise.REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.PromiseResult);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e)
                    }
                });
            } else {
                this.onFulfilledCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(this.PromiseResult);
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e);
                        }
                    });
                });
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.PromiseResult);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
                            reject(e);
                        }
                    });
                });
            }
        })
        return promise2
    }
}

function resolvePromise(promise2, x, resolve, reject) {
    if (x === promise2) {
        return reject(new TypeError('Chaining cycle detected for promise'));
    }
    if (x instanceof myPromise) {
        if (x.PromiseState === myPromise.PENDING) {
            x.then(y => {
                resolvePromise(promise2, y, resolve, reject)
            }, reject);
        } else if (x.PromiseState === myPromise.FULFILLED) {
            resolve(x.PromiseResult);
        } else if (x.PromiseState === myPromise.REJECTED) {
            reject(x.PromiseResult);
        }
    } else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        let then
        try {
            then = x.then;
        } catch (e) {
            return reject(e);
        }

        if (typeof then === 'function') {
            let called = false; 
            try {
                then.call(x, 
                    (a) =>{
                        if(called){
                            return
                        }
                        called = true
                        resolvePromise(promise2, a, resolve, reject)
                    },
                    (b) =>{
                        if(called){
                            return
                        }
                        called = true
                        reject(b)
                    })
            } catch (e) {
                if (called) return;
                called = true;
                reject(e);
            }
        } else {
            resolve(x);
        }
    } else {
        return resolve(x);
    }
}

测试

首先在代码最后面加上这个

myPromise.deferred = function () {
    let result = {};
    result.promise = new myPromise((resolve, reject) => {
        result.resolve = resolve;
        result.reject = reject;
    });
    return result;
}
module.exports = myPromise;

安装及运行测试工具

npm i promises-aplus-tests --save-dev

npm run promises-aplus-tests myPromise

参考

https://juejin.cn/post/7043758954496655397


Author: Maple
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source Maple !
  TOC