6

本文参考了Node.js 实践教程 - Promise 实现这个视频,并添加了自己的一些想法。

首先来看 Promise 的构造:

// 这里用 Prometheus 代替 Promise
let p = new Prometheus((resolve, reject) => {
    resolve('hello')
})

下面我们来实现它:

// 三种状态
const PENDING = Symbol()
const FULFILLED = Symbol()
const REJECTED = Symbol()

function Prometheus (fn) {
    // fn 必须是函数
    if (typeof fn !== 'function') {
        throw new Error('fn must be a function!')
    }

    let state = PENDING // 初始状态是 PENDING
    let value = null // 返回值

    function fulfill (result) {
        state = FULFILLED
        value = result
    }

    // 完成时调用的方法,这里做了容错
    function resolve (result) {
        try {
            fulfill(result)
        } catch (err) {
            reject(err)
        }
    }

    // 拒绝时调用的方法
    function reject (error) {
        state = REJECTED
        value = error
    }

    fn(resolve, reject)
}

第二步,实现 then 方法:

let p = new Prometheus((resolve, reject) => {
    resolve('hello')
})

p.then(val => {
    console.log(val)
})
// 三种状态
const PENDING = Symbol()
const FULFILLED = Symbol()
const REJECTED = Symbol()

function Prometheus (fn) {
    // fn 必须是函数
    if (typeof fn !== 'function') {
        throw new Error('fn must be a function!')
    }

    let state = PENDING // 初始状态是 PENDING
    let value = null // 返回值

    function fulfill (result) {
        state = FULFILLED
        value = result
    }

    // 完成时调用的方法,这里做了容错
    function resolve (result) {
        try {
            fulfill(result)
        } catch (err) {
            reject(err)
        }
    }

    // 拒绝时调用的方法
    function reject (error) {
        state = REJECTED
        value = error
    }

    this.then = function (onFulfill, onReject) {
        switch (state) {
            case FULFILLED:
                onFulfill(value)
                break
            case REJECTED:
                onReject(value)
                break
        }
    }

    fn(resolve, reject)
}

第三步,在 Promise 里使用异步

let p = new Prometheus((resolve, reject) => {
    setTimeout(() => {
        resolve('hello')
    }, 0)
})

p.then(val => {
    console.log(val)
})

直接运行上面的代码发现控制台没有打印出 hello,原因是 Prometheus 里的代码是异步执行,导致记下来执行 then 方法的时候,statePENDING,后面再执行 resolve 的时候就不会走到 onFulfill 了,所以我们要在 then 方法里添加 statePENDING 的分支判断,把 onFulfillonReject 存到一个变量中:

// 三种状态
const PENDING = Symbol()
const FULFILLED = Symbol()
const REJECTED = Symbol()

function Prometheus (fn) {
    // fn 必须是函数
    if (typeof fn !== 'function') {
        throw new Error('fn must be a function!')
    }

    let state = PENDING // 初始状态是 PENDING
    let value = null // 返回值
    let hanler = {}

    function fulfill (result) {
        state = FULFILLED
        value = result
        handler.onFulfill(result)
    }

    // 完成时调用的方法,这里做了容错
    function resolve (result) {
        try {
            fulfill(result)
        } catch (err) {
            reject(err)
        }
    }

    // 拒绝时调用的方法
    function reject (error) {
        state = REJECTED
        value = error
        handler.onReject(error)
    }

    this.then = function (onFulfill, onReject) {
        switch (state) {
            case FULFILLED:
                onFulfill(value)
                break
            case REJECTED:
                onReject(value)
                break
            case PENDING:
                handler = { onFulfill, onReject }
        }
    }

    fn(resolve, reject)
}

异步实现了,我们再回过头看看同步是否正常运行:

let p = new Prometheus((resolve, reject) => {
  resolve('hello')
})

p.then(val => {
    console.log(val)
})

发现报错信息:

TypeError: handler.onReject is not a function

因为同步执行的时候,fulfillhandler{},所以会报错。

// 三种状态
const PENDING = Symbol()
const FULFILLED = Symbol()
const REJECTED = Symbol()

function Prometheus (fn) {
    // fn 必须是函数
    if (typeof fn !== 'function') {
        throw new Error('fn must be a function!')
    }

    let state = PENDING // 初始状态是 PENDING
    let value = null // 返回值
    let handler = {}

    function fulfill (result) {
        state = FULFILLED
        value = result
        next(handler)
    }

    // 完成时调用的方法,这里做了容错
    function resolve (result) {
        try {
            fulfill(result)
        } catch (err) {
            reject(err)
        }
    }

    // 拒绝时调用的方法
    function reject (error) {
        state = REJECTED
        value = error
        next(handler)
    }

    function next({ onFulfill, onReject }) {
        switch (state) {
            case FULFILLED:
                onFulfill && onFulfill(value)
                break
            case REJECTED:
                onReject && onReject(value)
                break
            case PENDING:
                handler = { onFulfill, onReject }
        }
    }

    this.then = function (onFulfill, onReject) {
        next({onFulfill, onReject})
    }

    fn(resolve, reject)
}

现在同步也可以正常运行了,接下来看看多个 then 链式调用:

let p = new Prometheus((resolve, reject) => {
  resolve('hello')
})

p.then(val => {
    console.log(val)
    return 'world'
}).then(val => {
    console.log(val)
})

执行代码会发现如下报错信息:

TypeError: Cannot read property 'then' of undefined

原因是 then 方法没有返回 Promise

// 三种状态
const PENDING = Symbol()
const FULFILLED = Symbol()
const REJECTED = Symbol()

function Prometheus (fn) {
    // fn 必须是函数
    if (typeof fn !== 'function') {
        throw new Error('fn must be a function!')
    }

    let state = PENDING // 初始状态是 PENDING
    let value = null // 返回值
    let handler = {}

    function fulfill (result) {
        state = FULFILLED
        value = result
        next(handler)
    }

    // 完成时调用的方法,这里做了容错
    function resolve (result) {
        try {
            fulfill(result)
        } catch (err) {
            reject(err)
        }
    }

    // 拒绝时调用的方法
    function reject (error) {
        state = REJECTED
        value = error
        next(handler)
    }

    function next({ onFulfill, onReject }) {
        switch (state) {
            case FULFILLED:
                onFulfill && onFulfill(value)
                break
            case REJECTED:
                onReject && onReject(value)
                break
            case PENDING:
                handler = { onFulfill, onReject }
        }
    }

    this.then = function (onFulfill, onReject) {
        return new Prometheus((resolve, reject) => {
            next({
                onFulfill: val => {
                    resolve(onFulfill(val))
                },
                onReject: err => {
                    reject(onReject(err))
                }
            })
        })
    }

    fn(resolve, reject)
}

再次运行,正确打印出结果。

到此,一个非常简单的 Promise 就实现了,当然,这里其实还有很多细节没有考虑,具体还要参考 Promise/A+


张宗正
412 声望25 粉丝