Skip to content

根据Promise A+ 规范实现Promise #1

@KrisGuoQin

Description

@KrisGuoQin

Promise是一种异步解决方案,可以有效的解决基于回调函数形式而造成的回调地狱。在ES6中正式成为规范。在工作中会经常用到,同时在面试中也是必考的考点,所以我们不仅仅要掌握它的用法,也要明白它的原理。

Promise规范

Promise规范要求摘要

  1. promise只有三种状态,分别为 pending, fulfilled, rejected。
  2. 状态只能从 pending 变为 fulfilled, 或者从 pending 变为 rejected. 状态不可逆转。
  3. promise接受一个函数作为参数,执行该函数,并为该函数填充resolve, reject两个参数。执行成功调用resolve, 执行失败调用reject。
  4. promise必须提供then方法用于处理状态变更后的结果。并且then方法的返回结果为新的promise。
  5. then方法的参数为 onFulfilled, onRejected .完成状态调用onFulfilled,失败状态调用 onRejected。

Promise源码实现

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
const isFunc = params => typeof params === 'function'

class Promise {
    constructor(executor) {
        if (!isFunc(executor)) throw new Error('Promise only accept a function as a parameter')

        // 状态
        this._status = PENDING
        // 数据
        this._value = undefined
        // 成功回调队列
        this._fulfillQueues = []
        // 失败回调队列
        this._rejectQueues = []

        try {
            executor(this._resolve.bind(this), this._reject.bind(this))
        } catch (error) {
            this._reject(error)
        }
    }
    // resolve时候执行的函数
    _resolve(value) {
        if (this._status === PENDING) {
            this._status = FULFILLED
            this._value = value
            // 2.2.2.3 回调函数只能调用一次
            let cb = this._fulfillQueues.shift()
            while(cb) {
                cb(value)
                cb = this._fulfillQueues.shift()
            }
            // 队列被清空
        }
    }
    // reject时候执行的函数
    _reject(reason) {
        if (this._status === PENDING) {
            this._status = REJECTED
            this._value = reason
            // 2.2.3.3 回调函数只能调用一次
            let cb = this._rejectQueues.shift()
            while(cb) {
                cb(reason)
                cb = this._rejectQueues.shift()
            }
            // 队列被清空
        }
    }
    // then
    then(onFulfilled, onRejected) {
        // 2.2.1 onFulfilled, onRejected 都是可选的
        // 2.2.5 onFulfilled, onRejected 必须作为函数调用
        
        // 2.2.7.3 若 onFulfilled ,不是一个函数,当前promise1为完成状态,
        // 那promise2也是完成状态,且值promise1的 value
        // 2.2.7.3 若 onRejected ,不是一个函数,当前promise1为失败状态,
        // 那promise2也是失败状态,且值为promise1的 reason
        onFulfilled = isFunc(onFulfilled) ? onFulfilled : value => value
        onRejected = isFunc(onRejected) ? onRejected : reason => { throw reason }
        const context = this

        // 2.2.7 then必须返回一个promise
        const promise2 = new Promise(function(resolve, reject){
            if (context._status === FULFILLED) {
                // 2.2.2 如果 onFulfilled 是函数,必须在promise1 fulfilled之后调用,其参数为promise1的value
                // 2.2.4 onFulfilled,必须异步的调用
                // 3.1 可以使用微任务 MutationObserver 或者 process.nextTick. 或者使用宏任务 setTimeout , setImmediate.
                setTimeout(() => {
                    try {
                        // 2.2.7.1 如果 onFulfilled, onRejected 返回了值 x ,运行 
                        // the Promise Resolution Procedure [[Resolve]](promise2, x)
                        let x = onFulfilled(context._value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        // 2.2.7.2 如果 onFulfilled, onRejected 抛出了错误,那 promise2 就 reject(error)
                        reject(error)
                    }
                })
                return
            }
            if (context._status === REJECTED) {
                // 2.2.3 如果 onRejected 是函数,必须在promise1 rejected 之后调用,其参数为promise1的value
                // 2.2.4 onRejected ,必须异步的调用
                // 3.1 可以使用微任务 MutationObserver 或者 process.nextTick. 或者使用宏任务 setTimeout , setImmediate.
                setTimeout(() => {
                    try {
                        // 2.2.7.1 如果 onFulfilled, onRejected 返回了值 x ,运行 
                        // the Promise Resolution Procedure [[Resolve]](promise2, x)
                        let x = onRejected(context._value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        // 2.2.7.2 如果 onFulfilled, onRejected 抛出了错误,那 promise2 就 reject(error)
                        reject(error)
                    }
                })
                return
            }
            if (context._status === PENDING) {
                context._fulfillQueues.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(context._value)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    })
                })
                context._rejectQueues.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onRejected(context._value)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    })
                })
            }
        })
        return promise2
    }
    // 用于测试
    // defer
    static defer() {
        return defer()
    }
    static deferred() {
        return defer()
    }
    // promise 规范只要求我们必须实现 then  方法,但是es6规范还要求一些静态方法与实例方法,如
    // 静态方法:Promise.resolve, Promise.reject, Promise.all, Promise.race
    // 实例方法:catch, finally
    // 在ES6标准入门中还有 done 方法,面试题中还有要实现 retry 方法
    // catch
    catch(onRejected) {
        return this.then(undefined, onRejected)
    }
    // done
    // 总是处于回调链的尾端,保证抛出任何可能的错误
    done(onFulfilled, onRejected) {
        this.then(onFulfilled, onRejected)
            .catch(reason => {
                // 全局抛出一个错误
                setTimeout(() => { throw reason }, 0)
            })
    }
    // finally
    // 和 done 的最大区别是,接受一个普通的回调函数作为参数,该函数不管怎样都会执行
    finally(handle) {
        return this.then(
            value => Promise.resolve(handle()).then(() => value),
            reason => Promise.resolve(handle()).then(() => { throw reason })
        )
    }
    // staic resolve
    static resolve(value) {
        if(value instanceof Promise) return value
        return new Promise(resolve => resolve(value))
    }
    // static reject
    static reject(reason) {
        return new Promise((resolve, reject) => reject(reason))
    }
    // parameter #1, the function to return a Promise
    // parameter #2, the max retry times
    // parameter #3, the delay between each attempt
    static retry(fn, times, delay) {
        return new Promise((resolve, reject) => {
            function attempt() {
                fn().then(resolve)
                    .catch(err => {
                        if (times === 0) {
                            reject(err)
                        } else {
                            times--
                            setTimeout(() => {
                                attempt()
                            }, delay)
                        }
                    })
            }
            attempt()
        })
    }
    // static all
    all(promises) {
        return new Promise((resolve, reject) => {
            let count = 0
            let results = []
            let len = promises.length
            if (!promises.length) {
                // 空的可迭代对象,直接返回
                resolve(results)
            } else {
                for(let i = 0; i < len; i++) {
                    Promise.resolve(promises[i]).then(
                        value => {
                            results[i] = value
                            count++
                            // 所有promise都完成后, resolve
                            if(count === len) resolve(results)
                        },
                        // 有一个发生错误,直接reject
                        error => reject(error)
                    )
                }
            }
        })
    }
    // static race
    race(promises) {
        return new Promise((resolve, reject) => {
            for(let p of promises) {
                // 第一个 Promise 完成或者失败后,直接返回结果
                Promise.resolve(p).then(
                    value => resolve(value),
                    reason => reject(reason)
                )
            }
        })
    }
}
function resolvePromise(promise2, x, resolve, reject) {
    // 2.3.1 如果promise2和x是同一个对象,使用一个 TypeError 来 reject promise
    if (promise2 === x) {
        reject(new TypeError('Chaining cycle'))
    }
    if (x && typeof x === 'object' || isFunc(x)) {
        // 2.3.3.3.3 如果 resolvePromise 和 rejectPromise 都被调用了,或者
        // 相同的参数被多次调用了,那么只取第一次调用,后面的调用忽略掉
        let used;
        try {
            // 2.3.3.1 把 x.then 赋值给 then
            const then = x.then
            // 2.3.3.3 如果then是一个函数,调用它,把x作为this,
            // resolvePromise 作为第一个参数,rejectPromise 作为第二个参数
            if (isFunc(then)) {
                then.call(
                    x,
                    // 2.3.3.3.1 如果/当使用值y来调用resolvePromise时候,运行
                    // resolvePromise(promise2, y, resolve, reject)
                    y => {
                        if(used) return
                        used = true
                        resolvePromise(promise2, y, resolve, reject)
                    },
                    // 2.3.3.3.2 如果/当使用值r来调用 作为第一个参数,rejectPromise 时候,使用
                    // 值 r 来 reject promise
                    r => {
                        if(used) return
                        used = true
                        reject(r)
                    }
                )
            } else {
                // 2.3.3.4 如果 then 不是函数,使用 x 来 fulfill promsie
                if(used) return
                used = true
                resolve(x)
            }
        } catch (error) {
            // 2.3.3.2 如果 2.3.3.1发生了错误,使用error来reject promise
            if(used) return
            used = true
            reject(error)
        }
    } else {
        // 2.3.4 如果 x 不是对象或函数, 使用 x 来 fulfill promsie
        resolve(x)
    }
}
function defer() {
    const dfd = {}
    dfd.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve
        dfd.reject = reject
    })
    return dfd
}
module.exports = Promise

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions