手写promise核心逻辑

526 阅读6分钟

如果想要手写promsie,需要先弄明白promise有哪些成员以及promise做了什么,开整!

1、promise是一个类,需要传入一个执行器,这个执行器有2个参数resolvereject,并且执行器是立即调用的;有3种状态,存在status里面

  • pending:进行中

  • fulfilled:成功

  • rejected: 失败 上代码

class MyPromise {
    constructor(excutor) {
        excutor(this.resolve, this.reject)
    }
    status = PENDING;
    value = undefined;
    reason = undefined;
}

2、resolve用来将状态改为成功的状态fulfilled,并保存成功的值valuereject用来将状态改为失败的状态rejected,并保存失败的原因reason,需要注意的是状态只能由pending转为fulfilled或者由pending转为rejectd,这个过程是不可逆的,并且,状态一经改变,就不能再发生变化了。

class MyPromise {
    .... //省略代码
    value = undefined;
    reason = undefined;
    resolve = value => {
        if (this.status !== PENDING) return;
        this.status = FULFILLED;
        this.value = value
    }
    reject = reason => {
        if (this.status !== PENDING) return;
        this.status = REJECTED;
        this.reason = reason
    }
}
module.exports = MyPromise

3、promise还提供then方法执行回调函数,then方法接收2个参数,第1个参数为成功的回调函数successCallback,可以获取到resolve方法保存的value值;第2个参数为失败的回调函数failCallback,可以获取到reject方法保存的失败的原因reason。如果promise状态为fulfilled<则执行successCallback,如果promise状态为rejected,则执行failCallback方法。

class MyPromise {
    ... //省略代码
    then(successCallback, failCallBack) {
        if (this.status === FULFILLED) {
            successCallback(this.value)
        } else if (this.status === REJECTED) {
            failCallBack(this.reason)
        }
    }
}
module.exports = MyPromise

4、promise执行器里面是支持异步的,但是调用promise的时候then方法会直接执行,由于异步执行,所以promise的状态还未改变,这时候需要将then方法传入的回调函数保存起来,等到promise的状态不为pending的时候再调用回调函数

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    ...//省略代码
    successCallback = undefined;
    failCallBack = undefined;
    resolve = value => {
        if (this.status !== PENDING) return;
        this.status = FULFILLED;
        this.value = value
        this.successCallback(this.value)
    }
    reject = reason => {
        if (this.status !== PENDING) return;
        this.status = REJECTED;
        this.reason = reason
        this.failCallBack(this.reason)
    }
    then(successCallback, failCallBack) {
        if (this.status === FULFILLED) {
            successCallback(this.value)
        } else if (this.status === REJECTED) {
            failCallBack(this.reason)
        } else {
            this.successCallback = successCallback
            this.failCallBack = failCallBack
        }
    }
}
module.exports = MyPromise

5、then方法是支持多次调用的,上面回调函数的保存,如果执行器是同步执行的情况下,是没有问题的,但是如果是异步的,那么我们就需要存储多个回调函数,所以需要把this.successCallbackthis.failCallBack处理成数组,然后在状态改变之后,依次调用回调函数数组里面的成员

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    ...//省略代码
    successCallback = [];
    failCallBack = [];
    resolve = value => {
        if (this.status !== PENDING) return;
        this.status = FULFILLED;
        this.value = value
        while (this.successCallback.length) this.successCallback.shift()(this.value)
    }
    reject = reason => {
        if (this.status !== PENDING) return;
        this.status = REJECTED;
        this.reason = reason
        while (this.failCallBack.length) this.failCallBack.shift()(this.reason)
    }
    then(successCallback, failCallBack) {
        if (this.status === FULFILLED) {
            successCallback(this.value)
        } else if (this.status === REJECTED) {
            failCallBack(this.reason)
        } else {
            this.successCallback.push(successCallback)
            this.failCallBack.push(failCallBack)
        }
    }
}
module.exports = MyPromise

6、then方法支持链式调用,当前then拿到的值就是上一个then方法回调函数返回的值。因为then方法是promise提供的函数,所以要想实现链式调用,需要在then里面返回一个promise,并且通过这个返回的promise里面的resolve/reject来处理回调函数的返回值

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    ...//省略代码
    then(successCallback, failCallBack) {
        let promise2 = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                let x = successCallback(this.value)
                resolve(x)
            } else if (this.status === REJECTED) {
                let x = failCallBack(this.reason)
                reject(x)
            } else {
                this.successCallback.push(successCallback)
                this.failCallBack.push(failCallBack)
            }
        })
        return promise2
    }
}
module.exports = MyPromise

7、then的回调函数里面返回的可能是一个普通值,也可能是一个promise对象,如果是一个普通值,直接按上面的处理是没问题的,但是如果返回一个promise对象,我们就需要判断这个promise对象的状态,如果成功,则调用resolve,如果失败则调用reject

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    ... // 省略代码
    then(successCallback, failCallBack) {
        let promise2 = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                let x = successCallback(this.value)
                resolvePromise(x, resolve, reject)
            } else if (this.status === REJECTED) {
                let x = failCallBack(this.reason)
                resolvePromise(x, resolve, reject)
            } else {
                this.successCallback.push(successCallback)
                this.failCallBack.push(failCallBack)
            }
        })
        return promise2
    }
}
function resolvePromise (x, resolve, reject) {
    if (x instanceof MyPromise) {
        x.then(resolve, reject)
    } else {
        resolve(x)
    }
}
module.exports = MyPromise

8、then方法链式调用识别promise对象自返回,链式调用返回的promise不能为当前promise本身

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    ...//省略代码
    then(successCallback, failCallBack) {
        let promise2 = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                // 因为下面需要传入promise2,但是直接传的话,是获取不到promise2的,因为promise2还没定义完成,所以这里将这段代码处理成异步代码,就能获取到promise2
                setTimeout(() => {
                    let x = successCallback(this.value)
                    resolvePromise(promise2, x, resolve, reject)
                }, 0)
            } else if (this.status === REJECTED) {
                let x = failCallBack(this.reason)
                resolvePromise(promise2, x, resolve, reject)
            } else {
                this.successCallback.push(successCallback)
                this.failCallBack.push(failCallBack)
            }
        })
        return promise2
    }
}
function resolvePromise (promise2, x, resolve, reject) {
    if (x === promise2) {
        return reject(new TypeError('不能调用自身promise'))
    }
    if (x instanceof MyPromise) {
        x.then(resolve, reject)
    } else {
        resolve(x)
    }
}
module.exports = MyPromise

9、添加错误捕获增加代码的健壮性,异步调用方法补充

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(excutor) {
        try {
            excutor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e)
        }
    }
    status = PENDING;
    value = undefined;
    reason = undefined;
    successCallback = [];
    failCallBack = [];
    resolve = value => {
        if (this.status !== PENDING) return;
        this.status = FULFILLED;
        this.value = value
        while (this.successCallback.length) this.successCallback.shift()()
    }
    reject = reason => {
        if (this.status !== PENDING) return;
        this.status = REJECTED;
        this.reason = reason
        while (this.failCallBack.length) this.failCallBack.shift()(this.reason)
    }
    then(successCallback, failCallBack) {
        let promise2 = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                // 因为下面需要传入promise2,但是直接传的话,是获取不到promise2的,因为promise2还没定义完成,所以这里将这段代码处理成异步代码,就能获取到promise2
                setTimeout(() => {
                    try {
                        let x = successCallback(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                }, 0)
            } else if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = failCallBack(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                }, 0)
            } else {
                this.successCallback.push(() => {
                    setTimeout(() => {
                        try {
                            let x = successCallback(this.value)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)
                })
                this.failCallBack.push(() => {
                    setTimeout(() => {
                        try {
                            let x = failCallBack(this.reason)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)
                })
            }
        })
        return promise2
    }
}
function resolvePromise (promise2, x, resolve, reject) {
    if (x === promise2) {
        return reject(new TypeError('不能调用自身promise'))
    }
    if (x instanceof MyPromise) {
        x.then(resolve, reject)
    } else {
        resolve(x)
    }
}
module.exports = MyPromise

10、then方法的参数是可选参数,如果不传参数就等同于

.then(value =>value, reason => reason)

修改代码,将then的参数变成可选参数

const { reject } = require("lodash")

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    ... // 省略代码
    then(successCallback, failCallBack) {
        successCallback = successCallback ? successCallback : value => value;
        failCallBack = failCallBack ? failCallBack : reason => reason;
        let promise2 = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                // 因为下面需要传入promise2,但是直接传的话,是获取不到promise2的,因为promise2还没定义完成,所以这里将这段代码处理成异步代码,就能获取到promise2
                setTimeout(() => {
                    try {
                        let x = successCallback(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                }, 0)
            } else if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = failCallBack(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                }, 0)
            } else {
                this.successCallback.push(() => {
                    setTimeout(() => {
                        try {
                            let x = successCallback(this.value)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)
                })
                this.failCallBack.push(() => {
                    setTimeout(() => {
                        try {
                            let x = failCallBack(this.reason)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)
                })
            }
        })
        return promise2
    }
}
function resolvePromise (promise2, x, resolve, reject) {
    if (x === promise2) {
        return reject(new TypeError('不能调用自身promise'))
    }
    if (x instanceof MyPromise) {
        x.then(resolve, reject)
    } else {
        resolve(x)
    }
}
module.exports = MyPromise

11、实现Promise.all方法

  • all方法是一个静态方法
  • all方法接收一个数组参数
  • 可以通过.then方法接收返回结果,所以all方法返回一个promise
  • all返回结果的顺序跟传入参数的顺序是一致的,不管是不是异步调用的
const { reject } = require("lodash")

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    
    static all(array) {
        let result = [];
        let index = 0;
        return new MyPromise((resolve, reject) => {
            function addData(key, val) {
                index++
                result[key] = val;
                if (index === array.length) resolve(result)
            }
            for (let i = 0;i < array.length; i++) {
                let current = array[i];
                if (current instanceof MyPromise) {
                    current.then(value => addData(i, value), reason => reject(reason))
                } else {
                    addData(i, array[i])
                }
            }
        })
    }

module.exports = MyPromise

12、实现Promise.resolve方法,Promise.resolve方法可以将传入的参数转为promise

class MyPromise {
    
    static resolve(value) {
        if (value instanceof MyPromise) return value;
        return new MyPromise(resolve => resolve(value))
    }
  }

最后附上完整的代码

const { reject } = require("lodash")

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(excutor) {
        try {
            excutor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e)
        }
    }
    status = PENDING;
    value = undefined;
    reason = undefined;
    successCallback = [];
    failCallBack = [];
    resolve = value => {
        if (this.status !== PENDING) return;
        this.status = FULFILLED;
        this.value = value
        while (this.successCallback.length) this.successCallback.shift()()
    }
    reject = reason => {
        if (this.status !== PENDING) return;
        this.status = REJECTED;
        this.reason = reason
        while (this.failCallBack.length) this.failCallBack.shift()(this.reason)
    }
    then(successCallback, failCallBack) {
        successCallback = successCallback ? successCallback : value => value;
        failCallBack = failCallBack ? failCallBack : reason => reason;
        let promise2 = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                // 因为下面需要传入promise2,但是直接传的话,是获取不到promise2的,因为promise2还没定义完成,所以这里将这段代码处理成异步代码,就能获取到promise2
                setTimeout(() => {
                    try {
                        let x = successCallback(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                }, 0)
            } else if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = failCallBack(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                }, 0)
            } else {
                this.successCallback.push(() => {
                    setTimeout(() => {
                        try {
                            let x = successCallback(this.value)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)
                })
                this.failCallBack.push(() => {
                    setTimeout(() => {
                        try {
                            let x = failCallBack(this.reason)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)
                })
            }
        })
        return promise2
    }
    static all(array) {
        let result = [];
        let index = 0;
        return new MyPromise((resolve, reject) => {
            function addData(key, val) {
                index++
                result[key] = val;
                if (index === array.length) resolve(result)
            }
            for (let i = 0;i < array.length; i++) {
                let current = array[i];
                if (current instanceof MyPromise) {
                    current.then(value => addData(i, value), reason => reject(reason))
                } else {
                    addData(i, array[i])
                }
            }
        })
    }
    static resolve(value) {
        if (value instanceof MyPromise) return value;
        return new MyPromise(resolve => resolve(value))
    }
}
function resolvePromise (promise2, x, resolve, reject) {
    if (x === promise2) {
        return reject(new TypeError('不能调用自身promise'))
    }
    if (x instanceof MyPromise) {
        x.then(resolve, reject)
    } else {
        resolve(x)
    }
}
module.exports = MyPromise