從零實現一個簡單的 Promise

本文參考了Node.js 實踐教程 - Promise 實現這個視頻,並添加了本身的一些想法。javascript

首先來看 Promise 的構造:html

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

下面咱們來實現它:java

// 三種狀態
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 方法:promise

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 裏使用異步bash

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)
})

發現報錯信息:this

TypeError: handler.onReject is not a function

由於同步執行的時候,fulfillhandler{},因此會報錯。code

// 三種狀態
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+

相關文章
相關標籤/搜索