如何实现一个Promise

如何实现一个Promise

1 实现Promise构造函数

Promise自身包含一个状态属性PromiseStaus,一个异步结果属性PromiseResult
状态在pending、fulfilled、rejected之间枚举,改变状态的两个函数resolve、reject由Promise自身的执行器函数提供(根据A+规范这个executor执行器函数必传否则抛出异常),而Promise的结果由用户提供,且状态一旦从pending更改为其他便不可逆

1.1 关于executor

executor函数在Promise构造函数的执行就立即同步执行。在这个过程中,用户可以调用resolve和reject改变promise实例的状态
状态的改变可以是异步的,比如在定时器的回调中修改,或者在一个网络请求返回后修改,当然也可能是同步的,即直接同步修改
执行过程中一旦抛出异常,Promise内部便会捕获它,同时调用reject将其处理

1.2 Promise构造函数及其作用

Promise构造函数的作用:

  1. 生成Promise实例
  2. 向外暴露改变Promise实例状态的方法
  3. 捕获执行器函数抛出的异常,使用reject将其处理
  4. 根据1.1,如果状态的修改是异步的,那么需要将then中的回调函数存储在promise实例中,一旦状态改变就立即调用(后续then的实现中会说明)
const PromiseStatusMap = {
    pending: 'pending',
    fulfilled: 'fulfilled',
    rejected: 'rejected'
}

const isPendingStatus = status => status === PromiseStatusMap.pending
const isFunction = target => typeof target === 'function'
const doAsyncTask = callback => {
    setTimeout(() => {
        callback()
    }, 0)
}

function Promise(executor) {
    if (typeof executor !== 'function') {
        throw 'the first parameter must be a function.'
    }

    this.PromiseResult = null
    this.PromiseStatus = PromiseStatusMap.pending
    this.callbacks = []

    const onFulfilled = (value) => {
        // 状态一但改变不可再次更改
        if (!isPendingStatus(this.PromiseStatus)) {
            return
        }
        this.PromiseResult = value
        this.PromiseStatus = PromiseStatusMap.fulfilled
        doAsyncTask(() => {
            this.callbacks.forEach(handleCallback => {
                handleCallback()
            })
        })
    }

    const onRejected = (reason) => {
        // 状态一但改变不可再次更改
        if (!isPendingStatus(this.PromiseStatus)) {
            return
        }

        this.PromiseResult = reason
        this.PromiseStatus = PromiseStatusMap.rejected
        doAsyncTask(() => {
            this.callbacks.forEach(handleCallback => {
                handleCallback()
            })
        })
    }

    try {
        // 改变状态的方法由promise提供,方法的参数由用户提供给promise处理
        executor(onFulfilled, onRejected)
    } catch (error) {
        // 一旦抛出异常,状态变为失败
        console.warn(error)
        onRejected(error)
    }
}

2 实现then

2.1 关于then

then是promise原型上的方法,它将Promise成功或者失败的回调提供给用户,同时将promise的结果注入到回调函数的形参中,让用户能在promise得到结果之后去处理接下来的事情

在promise中,then中的回调是异步执行的

2.2 then到底做了什么事情

在promise中,then要做什么事情,取决于promise的状态。根据1.1可知,then回调执行时,promise的状态可能已经改变,也可能尚未改变

  1. 如果状态的改变是同步的,即then执行的时候promise的状态不为pending,那么then需要根据promise的状态分别执行成功或失败的回调,并将promise结果值注入到回调的形参中去
  2. 如果状态的修改是异步的,即then执行的时候promise的状态为pending,那么then需要将用户提供的回调存储起来,等待promise的状态改变的时候再执行对应的回调

简而言之,then就做了两件事情。一是在promise拿到结果之后执行对应的成功或失败的回调,二是对外返回一个新的promise

2.3 then的返回值

then一定返回一个新的promise,这个新的promise根据成功或失败回调的返回值callbackResult决定

  1. 如果用户没有定义成功/失败的回调,那么给一个默认的function,实现then链式传递或异步中断的效果

  2. 如果callbackResult的返回值是Promise类型,那么then的返回值状态和结果与callbackResult保持一致

  3. 如果callbackResult的返回值不是Promise类型,则返回一个成功的Promise

  4. 如果抛出异常,则调用reject,返回一个失败的promise

2.4 实现then

根据以上,为promise的原型添加then方法

Promise.prototype.then = function (onFulfilled, onRejected) {

    return new Promise((resolve, reject) => {

        // 2.3.1
        !isFunction(onFulfilled) && (onFulfilled = () => { })
        !isFunction(onRejected) && (onRejected = (reason) => { throw reason })

        // 根据promise状态去执行成功/失败的回调,并且注入相应的promise结果
        const handleCallback = () => {
            try {
                const fn = this.PromiseStatus === PromiseStatusMap.rejected ? onRejected : onFulfilled
                const callbackResult = fn(this.PromiseResult)
                if (callbackResult instanceof Promise) {
                    callbackResult.then(v => {
                        resolve(v)
                    }, r => {
                        reject(r)
                    })
                } else {
                    resolve(callbackResult)
                }
            } catch (error) {
                console.warn(error)
                reject(error)
            }
        }

        // 2.2.1  如果then执行的时候状态已经改变
        if (this.PromiseStatus !== PromiseStatusMap.pending) {
            // 2.1 回调的执行是异步的
            doAsyncTask(handleCallback)
        } else {
            // pending状态
            // 2.2.2
            this.callbacks.push(handleCallback)
        }

    })
}

3 实现静态方法resolve与reject

Promise.resolve返回一个成功的promise
Promise.reject返回一个失败的promise
如果参数是一个promise,那么采用这个promise的状态与结果

Promise.resolve = function(result){
    return new Promise((resolve,reject)=>{
        if(result instanceof Promise){
            result.then((value)=>{
                resolve(value)
            },(reason)=>{
                console.log('here')
                reject(reason)
            })
        }else{
            resolve(result)
        }
    })
}

Promise.reject = function(result){
    return new Promise((resolve,reject)=>{
        if(result instanceof Promise){
            result.then(value=>{
                resolve(value)
            },reason=>{
                reject(reason)
            })
        }else{
            reject(result)
        }
    })
}

4 实现catch方法

执行回调的过程中,异常已经被handleCallback中的trycatch捕获并reject处理,因此只需要传递一个失败的回调给then即可

Promise.prototype.catch = function(reason){
    return new Promise((undefined,reject)=>{
        reject(reason)
    })
}

5 实现静态方法all

all用于并行执行异步操作,接受一个可迭代的promises对象作为参数,返回一个新的Promise
只有全部异步操作都成功时成功,有一个异步操作失败就失败
如果成功,则返回的成功promise与传入的promises顺序保持一致

需要注意:

  1. 输入判断。接受的参数是一个可迭代的类型,并不一定是数组,因此需要先判断入参是否可迭代,并且使用forof迭代entries属性以便同时拿到索引和元素
  2. 为了保证输入与输出的顺序一致,我们需要根据索引去赋值PromiseResult数组中的元素。但是在判定所有promise都得到结果的时候,我们不应该用.length属性做判断,因为js数组的长度是可以被随意更改指定的,而且我们不确定哪个promise会先完成,也就是说,我们数组长度和完成的异步任务的数量并不一定对等,因此需要创建一个递增的变量count来作这件事情。
  3. 异常捕获,一旦抛出异常就调用reject将其处理
Promise.all = function (promises) {
    if (typeof promises?.[Symbol.iterator] !== 'function') {
        throw 'the first parameter must be iterable(need a Symbol.iterator property)'
    }
    return new Promise((resolve, reject) => {
        try {
            const result = []
            // 根据索引赋值,不能用result.length做判断,因为我们不确定哪个promise会先完成
            // arr[2] = 1  我们可能会返回:arr:[empty,empty,1]
            let count = 0
            for (const [index, promiseItem] of promises.entries()) {

                if (promiseItem instanceof Promise) {
                    promiseItem.then(value => {
                        result[index] = value
                        ++count === promises.length && resolve(result)
                    }, reason => {
                        reject(reason)
                    })
                } else {
                    result[index] = promiseItem
                    ++count === promises.length && resolve(result)
                }
            }
        } catch (error) {
            console.warn(error)
            reject(error)
        }
    })

}

5.1 实现静态方法allSettled

对于all,所有异步任务成功才成功,有一个失败就返回失败,而有时候我们的场景可能是这样的:即使某些任务失败了,也将所有异步任务的结果返回。这就是allSettled的用途

在all的基础上实现allSettled其实非常容易,我们只需将"一旦失败就返回一个失败的promise"这个逻辑改成"不论成功或失败,所有异步任务得到结果才返回一个新的Promise"即可

const judgeResolve = ()=> ++count === promises.length && resolve(result)
if (promiseItem instanceof Promise) {
    promiseItem.then(value => {
        result[index] = { status: 'fulfilled', value }
        judgeResolve()
    }, reason => {
        result[index] = { status: 'rejected', reason }
        judgeResolve()
    })
} else {
    result[index] = { status: 'fulfilled', value: promiseItem }
    judgeResolve()
}

6 实现静态方法race

和all一样,race方法接受一个可迭代的数据作为形参,返回一个新的promise,返回的promise值采用第一个得到结果的promise的result值

Promise.race = function (promises) {
    if (typeof promises?.[Symbol.iterator] !== 'function') {
        throw 'the first parameter must be iterable(need a Symbol.iterator property)'
    }
    return new Promise((resolve, reject) => {
        try {
            for (const promiseItem of promises) {
                if (promiseItem instanceof Promise) {
                    promiseItem.then((value) => {
                        resolve(value)
                    }, (reasom) => {
                        reject(reasom)
                    })
                } else {
                    resolve(promiseItem)
                }
            }
        } catch (error) {
            console.warn(error)
            reject(error)
        }
    })
}

7 实现finally方法

Promise.prototype.finally = function(fn){
    return new Promise((resolve,reject)=>{
        this.then((value)=>{
            resolve(value)
        },(reason)=>{
            reject(reason)
        })
    })
}

8 代码汇总

const PromiseStatusMap = {
    pending: 'pending',
    fulfilled: 'fulfilled',
    rejected: 'rejected'
}

const isPendingStatus = status => status === PromiseStatusMap.pending
const isFunction = target => typeof target === 'function'
const doAsyncTask = callback => {
    setTimeout(() => {
        callback()
    }, 0)
}

function Promise(executor) {
    if (typeof executor !== 'function') {
        throw 'the first parameter must be a function.'
    }

    this.PromiseResult = undefined
    this.PromiseStatus = PromiseStatusMap.pending
    this.callbacks = []

    const onFulfilled = (value) => {
        // 状态一但改变不可再次更改
        if (!isPendingStatus(this.PromiseStatus)) {
            return
        }
        this.PromiseResult = value
        this.PromiseStatus = PromiseStatusMap.fulfilled
        doAsyncTask(() => {
            this.callbacks.forEach(handleCallback => {
                handleCallback()
            })
        })
    }

    const onRejected = (reason) => {
        // 状态一但改变不可再次更改
        if (!isPendingStatus(this.PromiseStatus)) {
            return
        }

        this.PromiseResult = reason
        this.PromiseStatus = PromiseStatusMap.rejected
        doAsyncTask(() => {
            this.callbacks.forEach(handleCallback => {
                handleCallback()
            })
        })
    }

    try {
        // 改变状态的方法由promise提供,方法的参数由用户提供给promise处理
        executor(onFulfilled, onRejected)
    } catch (error) {
        // 一旦抛出异常,状态变为失败
        console.warn(error)
        onRejected(error)
    }
}

Promise.prototype.then = function (onFulfilled, onRejected) {

    return new Promise((resolve, reject) => {

        // 2.3.1
        !isFunction(onFulfilled) && (onFulfilled = () => { })
        !isFunction(onRejected) && (onRejected = (reason) => { throw reason })

        // 根据promise状态去执行成功/失败的回调,并且注入相应的promise结果
        const handleCallback = () => {
            try {
                const fn = this.PromiseStatus === PromiseStatusMap.rejected ? onRejected : onFulfilled
                const callbackResult = fn(this.PromiseResult)
                if (callbackResult instanceof Promise) {
                    callbackResult.then(v => {
                        resolve(v)
                    }, r => {
                        reject(r)
                    })
                } else {
                    resolve(callbackResult)
                }
            } catch (error) {
                console.warn(error)
                reject(error)
            }
        }

        // 2.2.1  如果then执行的时候状态已经改变
        if (this.PromiseStatus !== PromiseStatusMap.pending) {
            // 2.1 回调的执行是异步的
            doAsyncTask(handleCallback)
        } else {
            // pending状态
            // 2.2.2
            this.callbacks.push(handleCallback)
        }

    })
}

Promise.resolve = function (result) {
    return new Promise((resolve, reject) => {
        if (result instanceof Promise) {
            result.then((value) => {
                resolve(value)
            }, (reason) => {
                reject(reason)
            })
        } else {
            resolve(result)
        }
    })
}

Promise.reject = function (result) {
    return new Promise((resolve, reject) => {
        if (result instanceof Promise) {
            result.then(value => {
                resolve(value)
            }, reason => {
                reject(reason)
            })
        } else {
            reject(result)
        }
    })
}

Promise.prototype.catch = function (reason) {
    return new Promise((undefined, reject) => {
        reject(reason)
    })
}

Promise.all = function (promises) {
    if (typeof promises?.[Symbol.iterator] !== 'function') {
        throw 'the first parameter must be iterable(need a Symbol.iterator property)'
    }
    return new Promise((resolve, reject) => {
        try {
            const result = []
            // 根据索引赋值,不能用result.length做判断,因为我们不确定哪个promise会先完成
            // arr[2] = 1  我们可能会返回:arr:[empty,empty,1]
            let count = 0
            for (const [index, promiseItem] of promises.entries()) {

                if (promiseItem instanceof Promise) {
                    promiseItem.then(value => {
                        result[index] = value
                        ++count === promises.length && resolve(result)
                    }, reason => {
                        reject(reason)
                    })
                } else {
                    result[index] = promiseItem
                    ++count === promises.length && resolve(result)
                }
            }
        } catch (error) {
            console.warn(error)
            reject(error)
        }
    })

}

Promise.race = function (promises) {
    if (typeof promises?.[Symbol.iterator] !== 'function') {
        throw 'the first parameter must be iterable(need a Symbol.iterator property)'
    }
    return new Promise((resolve, reject) => {
        try {
            for (const promiseItem of promises) {
                if (promiseItem instanceof Promise) {
                    promiseItem.then((value) => {
                        resolve(value)
                    }, (reasom) => {
                        reject(reasom)
                    })
                } else {
                    resolve(promiseItem)
                }
            }
        } catch (error) {
            console.warn(error)
            reject(error)
        }
    })
}

Promise.prototype.finally = function(fn){
    return new Promise((resolve,reject)=>{
        this.then((value)=>{
            resolve(value)
        },(reason)=>{
            reject(reason)
        })
    })
}

module.exports = {
    Promise
}

posted @ 2020-02-28 01:19  IslandZzzz  阅读(230)  评论(0编辑  收藏  举报