溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

怎么使用JavaScript手寫(xiě)一個(gè)Promise

發(fā)布時(shí)間:2022-07-14 09:31:33 來(lái)源:億速云 閱讀:182 作者:iii 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要介紹“怎么使用JavaScript手寫(xiě)一個(gè)Promise”的相關(guān)知識(shí),小編通過(guò)實(shí)際案例向大家展示操作過(guò)程,操作方法簡(jiǎn)單快捷,實(shí)用性強(qiáng),希望這篇“怎么使用JavaScript手寫(xiě)一個(gè)Promise”文章能幫助大家解決問(wèn)題。

    Promise核心原理實(shí)現(xiàn)

    首先我們從使用的角度來(lái)分析一下Promise,然后編寫(xiě)一個(gè)最簡(jiǎn)單版本的Promise。

    Promise的使用分析

    Promise就是一個(gè)類(lèi),在執(zhí)行這個(gè)類(lèi)的時(shí)候,需要傳遞一個(gè)執(zhí)行器(回調(diào)函數(shù))進(jìn)去,執(zhí)行器會(huì)立即執(zhí)行。

    Promise中的狀態(tài)分為三個(gè),分別是:

    • pending→等待

    • fulfilled→成功

    • rejected→失敗

    狀態(tài)的切換只有兩種,分別是:

    • pending→fulfilled

    • pending→rejected

    一旦狀態(tài)發(fā)生改變,就不會(huì)再次改變:

    • 執(zhí)行器中的兩個(gè)參數(shù),分別是resolve和reject,其實(shí)就是兩個(gè)回調(diào)函數(shù),調(diào)用resolve是從pending狀態(tài)到fulfilled,調(diào)用reject是從狀態(tài)pending到rejected。傳遞給這兩個(gè)回調(diào)函數(shù)的參數(shù)會(huì)作為成功或失敗的值。

    • Promise的實(shí)例對(duì)象具有一個(gè)then方法,該方法接受兩個(gè)回調(diào)函數(shù),分別來(lái)處理成功與失敗的狀態(tài),then方法內(nèi)部會(huì)進(jìn)行判斷,然后根據(jù)當(dāng)前狀態(tài)確定調(diào)用的回調(diào)函數(shù)。then方法應(yīng)該是被定義在原型對(duì)象中的。

    • then的回調(diào)函數(shù)中都包含一個(gè)值,如果是成功,表示成功后返回的值;如果是失敗就表示失敗的原因。

    MyPromise的實(shí)現(xiàn)

    根據(jù)我們上面的分析,寫(xiě)出如下代碼:

    MyPromise.js
    // 定義所有狀態(tài)的常量
    const PENDING = 'pending'
    const FULFILLED = 'fulfilled'
    const REJECTED = 'rejected'
    // Promise實(shí)質(zhì)上就是一個(gè)類(lèi),首先創(chuàng)建一個(gè)Promise的類(lèi)
    class MyPromise {
        // 實(shí)例化Promise時(shí)需要一個(gè)回調(diào)函數(shù),該回調(diào)函數(shù)立即執(zhí)行
        constructor(executor) {
            // 在調(diào)用executor需要傳遞兩個(gè)回調(diào)函數(shù),分別是resolve和reject
            executor(this.resolve, this.reject)
        }
        // Promise 的狀態(tài)
        status = PENDING
        // 記錄成功與失敗的值
        value = undefined
        reason = undefined
        resolve = (value) => {// 這里使用箭頭函數(shù)是為了使其內(nèi)部的this指向?yàn)樵搶?shí)例化后的對(duì)象
            // 形參value表示,調(diào)用resolve時(shí)傳遞的參數(shù)
            // 如果當(dāng)前狀態(tài)不是pending,就直接跳出該邏輯
            if (this.status !== PENDING) return
    
            // 將狀態(tài)修改為成功
            this.status = FULFILLED
    
            // 將傳入的值進(jìn)行保存
            this.value = value
        }
        reject = (reason) => {// 這里使用箭頭函數(shù)是為了使其內(nèi)部的this指向?yàn)樵搶?shí)例化后的對(duì)象
            // 形參reason表示,調(diào)用reject時(shí)傳遞的失敗的原因
            // 如果當(dāng)前狀態(tài)不是pending,就直接跳出該邏輯
            if (this.status !== PENDING) return
    
            // 將狀態(tài)修改為失敗
            this.status = REJECTED
    
            // 保存失敗的原因
            this.reason = reason
        }
        // then方法的實(shí)現(xiàn)
        then (onFulfilled, onRejected) {
            // 判斷當(dāng)前狀態(tài),根據(jù)狀態(tài)調(diào)用指定回調(diào)
            if (this.status === FULFILLED) {
                // 將成功的值作為參數(shù)返回
                onFulfilled(this.value)
            } else if (this.status === REJECTED) {
                // 將失敗的原因作為參數(shù)返回
                onRejected(this.reason)
            }
        }
    }
    // 導(dǎo)出Promise
    module.exports = MyPromise

    現(xiàn)在我們就來(lái)寫(xiě)一段代碼驗(yàn)證一下上面的代碼

    驗(yàn)證resolve:

    const MyPromise = require('./myPromise')
    
    let promise = new MyPromise((resolve, reject) => {
        resolve('成功')
    })
    promise.then(value => {
        console.log(value);
    }, reason => {
        console.log(reason);
    })
    /* 輸出
        成功
    */

    驗(yàn)證reject:

    const MyPromise = require('./myPromise')
    let promise = new MyPromise((resolve, reject) => {
        reject('失敗')
    })
    promise.then(value => {
        console.log(value);
    }, reason => {
        console.log(reason);
    })
    /* 輸出
        失敗
    */

    驗(yàn)證狀態(tài)不可變:

    const MyPromise = require('./myPromise')
    let promise = new MyPromise((resolve, reject) => {
        resolve('成功')
        reject('失敗')
    })
    promise.then(value => {
        console.log(value);
    }, reason => {
        console.log(reason);
    })
    /* 輸出
        成功
    */

    在Promise中加入異步操作

    如果我們的代碼中存在異步操作,我們自己寫(xiě)的Promise將毫無(wú)用處,

    例如下面這段代碼:

    const MyPromise = require('./myPromise')
    let promise = new MyPromise((resolve, reject) => {
        setTimeout(resolve, 2000, '成功')
    })
    promise.then(value => {
        console.log(value);
    }, reason => {
        console.log(reason);
    })

    這段代碼2000ms后沒(méi)有任何輸出,為了解決這個(gè)問(wèn)題我們將對(duì)自己編寫(xiě)的類(lèi)進(jìn)行如下操作:

    • 創(chuàng)建兩個(gè)實(shí)例方法用于存儲(chǔ)我們傳入的成功與失敗的處理邏輯。

    • then方法添加狀態(tài)為pending時(shí)的處理邏輯,這時(shí)將傳遞進(jìn)來(lái)的屬性保存到實(shí)例上。

    • 在成功或者失敗時(shí)調(diào)用相應(yīng)的傳遞進(jìn)來(lái)的回調(diào)函數(shù)(實(shí)例屬性存在函數(shù)的情況下)。

    實(shí)現(xiàn)代碼如下:

    // MyPromise.js
    const PENDING = 'pending'
    const FULFILLED = 'fulfilled'
    const REJECTED = 'rejected'
    class MyPromise {
        constructor(executor) {
            executor(this.resolve, this.reject)
        }
        status = PENDING
        value = undefined
        reason = undefined
        // 存儲(chǔ)成功與失敗的處理邏輯
        onFulfilled = undefined
        onRejected = undefined
        resolve = (value) => {
            if (this.status !== PENDING) return
            this.status = FULFILLED
            this.value = value
            // 如果將狀態(tài)修復(fù)為成功,調(diào)用成功的回調(diào)
            this.onFulfilled && this.onFulfilled(this.value)
        }
        reject = (reason) => {
            if (this.status !== PENDING) return
            this.status = REJECTED
            this.reason = reason
            // 如果將狀態(tài)修復(fù)為失敗,調(diào)用失敗的回調(diào)
            this.onRejected && this.onRejected(this.reason)
        }
        then (onFulfilled, onRejected) {
            if (this.status === FULFILLED) {
                onFulfilled(this.value)
            } else if (this.status === REJECTED) {
                onRejected(this.reason)
            } else {
                // 表示既不是成功,也不是失敗。這個(gè)時(shí)候保存?zhèn)鬟f進(jìn)來(lái)的兩個(gè)回調(diào)
                this.onFulfilled = onFulfilled
                this.onRejected = onRejected
            }
        }
    }
    module.exports = MyPromise

    這里的this.onFulfilled && this.onFulfilled(this.value)表示如果該屬性存在就調(diào)用這個(gè)方法。

    現(xiàn)在我們重新執(zhí)行一開(kāi)始上面那一段代碼,2s后會(huì)成功輸出成功

    實(shí)現(xiàn)then方法的多次調(diào)用

    Promise實(shí)例中存在要給then方法,允許我們?cè)赑romise實(shí)例中鏈?zhǔn)秸{(diào)用,每個(gè)then方法還會(huì)返回一個(gè)Promise實(shí)例,

    如下圖所示:

    怎么使用JavaScript手寫(xiě)一個(gè)Promise

    Promise實(shí)例方法then是可以多次進(jìn)行調(diào)用,而我們現(xiàn)在自己封裝的卻執(zhí)行調(diào)用一次,現(xiàn)在根據(jù)新需要來(lái)重新改寫(xiě)我們的代碼,

    實(shí)現(xiàn)思路如下:

    • 定義可以存儲(chǔ)多個(gè)回調(diào)的數(shù)組,用于存儲(chǔ)多個(gè)回調(diào)函數(shù)。

    • 如果是同步執(zhí)行的代碼,執(zhí)行后立即知道執(zhí)行結(jié)果,所以可以直接調(diào)用回調(diào)函數(shù)。

    • 如果是異步代碼,需要將每次回調(diào)函數(shù)保存到數(shù)組中,然后狀態(tài)變化時(shí)依次調(diào)用函數(shù)。

    實(shí)現(xiàn)代碼如下:

    // MyPromise.js
    const PENDING = 'pending'
    const FULFILLED = 'fulfilled'
    const REJECTED = 'rejected'
    class MyPromise {
        constructor(executor) {
            executor(this.resolve, this.reject)
        }
        status = PENDING
        value = undefined
        reason = undefined
        // 存儲(chǔ)成功與失敗的處理邏輯
        onFulfilled = []
        onRejected = []
    
        resolve = (value) => {
            if (this.status !== PENDING) return
            this.status = FULFILLED
            this.value = value
    
            // 如果將狀態(tài)修復(fù)為成功,調(diào)用成功的回調(diào)
            while (this.onFulfilled.length) {
                // Array.prototype.shift() 用于刪除數(shù)組第一個(gè)元素,并返回
                this.onFulfilled.shift()(this.value)
            }
        }
        reject = (reason) => {
            if (this.status !== PENDING) return
            this.status = REJECTED
            this.reason = reason
            // 如果將狀態(tài)修復(fù)為失敗,調(diào)用失敗的回調(diào)
            while (this.onRejected.length) {
                // Array.prototype.shift() 用于刪除數(shù)組第一個(gè)元素,并返回
                this.onRejected.shift()(this.reason)
            }
        }
        then (onFulfilled, onRejected) {
            if (this.status === FULFILLED) {
                onFulfilled(this.value)
            } else if (this.status === REJECTED) {
                onRejected(this.reason)
            } else {
                // 表示既不是成功,也不是失敗。這個(gè)時(shí)候保存?zhèn)鬟f進(jìn)來(lái)的兩個(gè)回調(diào)
                this.onFulfilled.push(onFulfilled)
                this.onRejected.push(onRejected)
            }
        }
    }
    module.exports = MyPromise

    這里我們通過(guò)數(shù)組的shift()方法,該方法刪除數(shù)組的第一個(gè)元素,并返回,返回的這個(gè)值正好是一個(gè)回調(diào)函數(shù),然后調(diào)用該函數(shù)即可實(shí)現(xiàn)該功能。

    實(shí)現(xiàn)then的鏈?zhǔn)秸{(diào)用

    想要實(shí)現(xiàn)then的鏈?zhǔn)秸{(diào)用,主要解決兩個(gè)問(wèn)題:

    • 返回的是一個(gè)新的MyPormise實(shí)例。

    • then的返回值作為下一次的鏈?zhǔn)秸{(diào)用的參數(shù)。

    這里分為兩種情況:

    • 直接返回一個(gè)值,可以直接作為值使用

    • 返回一個(gè)新的MyPormise實(shí)例,此時(shí)就需要判斷其狀態(tài)

    實(shí)現(xiàn)代碼如下:

    // MyPromise.js
    /* 省略的代碼同上 */
    class MyPromise {
        /* 省略的代碼同上 */
    
        // then方法的實(shí)現(xiàn)
        then (onFulfilled, onRejected) {
            // then 方法返回一個(gè)MyPromise實(shí)例
            return new MyPromise((resolve, reject) => {
                // 判斷當(dāng)前狀態(tài),根據(jù)狀態(tài)調(diào)用指定回調(diào)
                if (this.status === FULFILLED) {
                    // 將成功的值作為參數(shù)返回
                    // 保存執(zhí)行回調(diào)函數(shù)的結(jié)果
                    const result = onFulfilled(this.value)
    
                    // 如果返回的是一個(gè)普通的值,直接調(diào)用resolve
                    // 如果是一個(gè)MyPromise實(shí)例,根據(jù)返回的解決來(lái)決定是調(diào)用resolve,還是reject
                    resolvePromise(result, resolve, reject)
                } else if (this.status === REJECTED) {
                    // 將失敗的原因作為參數(shù)返回
                    onRejected(this.reason)
                } else {
                    // 表示既不是成功,也不是失敗。這個(gè)時(shí)候保存?zhèn)鬟f進(jìn)來(lái)的兩個(gè)回調(diào)
                    this.onFulfilled.push(onFulfilled)
                    this.onRejected.push(onRejected)
                }
            })
        }
    }
    function resolvePromise (result, resolve, reject) {
        // 判斷傳遞的result是不是MyPromise的實(shí)例對(duì)象
        if (result instanceof MyPromise) {
            // 說(shuō)明是MyPromise的實(shí)例對(duì)象
            // 調(diào)用.then方法,然后在回調(diào)函數(shù)中獲取到具體的值,然后調(diào)用具體的回調(diào)
            // result.then(value => resolve(value), reason => reject(reason))
            // 簡(jiǎn)寫(xiě)
            result.then(resolve, reject)
        } else {
            resolve(result)
        }
    }
    module.exports = MyPromise

    then方法鏈?zhǔn)秸{(diào)用識(shí)別Promise對(duì)象自返回

    在Promise中,如果then方法返回的是自己的promise對(duì)象,則會(huì)發(fā)生promise的嵌套,這個(gè)時(shí)候程序會(huì)報(bào)錯(cuò),

    測(cè)試代碼如下:

    var promise = new Promise((resolve, reject) => {
      resolve(100)
    })
    var p1 = promise.then(value => {
      console.log(value)
      return p1
    })
    // 100
    // Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>

    想要解決這個(gè)問(wèn)題其實(shí)我們只需要在then方法返回的MyPromise實(shí)例對(duì)象與then中回調(diào)函數(shù)返回的值進(jìn)行比對(duì),如果相同的返回一個(gè)reject的MyPromise實(shí)例對(duì)象,并創(chuàng)建一個(gè)TypeError類(lèi)型的Error。

    實(shí)現(xiàn)代碼如下:

    // MyPromise.js
        /* 省略的代碼同上 */
        then (onFulfilled, onRejected) {
            // then 方法返回一個(gè)MyPromise實(shí)例
            const promise = new MyPromise((resolve, reject) => {
                // 判斷當(dāng)前狀態(tài),根據(jù)狀態(tài)調(diào)用指定回調(diào)
                if (this.status === FULFILLED) {
                    // 這里并不需要延遲執(zhí)行,而是通過(guò)setTimeout將其變成異步函數(shù)
                    // 如果不變成異步的話是在函數(shù)內(nèi)獲取不到promise的
                    setTimeout(() => {
                        // 將成功的值作為參數(shù)返回
                        // 保存執(zhí)行回調(diào)函數(shù)的結(jié)果
                        const result = onFulfilled(this.value)
    
                        // 如果返回的是一個(gè)普通的值,直接調(diào)用resolve
                        // 如果是一個(gè)MyPromise實(shí)例,根據(jù)返回的解決來(lái)決定是調(diào)用resolve,還是reject
                        resolvePromise(promise, result, resolve, reject)
                    }, 0)
                } else if (this.status === REJECTED) {
                    // 將失敗的原因作為參數(shù)返回
                    onRejected(this.reason)
                } else {
                    // 表示既不是成功,也不是失敗。這個(gè)時(shí)候保存?zhèn)鬟f進(jìn)來(lái)的兩個(gè)回調(diào)
                    this.onFulfilled.push(onFulfilled)
                    this.onRejected.push(onRejected)
                }
            })
            return promise
        }
    }
    function resolvePromise (promise, result, resolve, reject) {
        // 這里修改一下該函數(shù),如果return的Promise實(shí)例對(duì)象,也就是傳入的promise===result的話,說(shuō)明在promise中return的是當(dāng)前promise對(duì)象。
        if (promise === result) {
            // 這里調(diào)用reject,并拋出一個(gè)Error
            // return 是必須的,阻止程序向下執(zhí)行
            return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
        }
        // 判斷傳遞的result是不是MyPromise的實(shí)例對(duì)象
        if (result instanceof MyPromise) {
            // 說(shuō)明是MyPromise的實(shí)例對(duì)象
            // 調(diào)用.then方法,然后在回調(diào)函數(shù)中獲取到具體的值,然后調(diào)用具體的回調(diào)
            // result.then(value => resolve(value), reason => reject(reason))
            // 簡(jiǎn)寫(xiě)
            result.then(resolve, reject)
        } else {
            resolve(result)
        }
    }
    module.exports = MyPromise

    這里then方法中的setTimeout的作用并不是延遲執(zhí)行,而是為了調(diào)用resolvePromise函數(shù)時(shí),保證創(chuàng)建的promise存在。

    捕獲錯(cuò)誤及 then 鏈?zhǔn)秸{(diào)用其他狀態(tài)代碼補(bǔ)充

    到目前為止我們現(xiàn)實(shí)的Promise并沒(méi)有對(duì)異常做任何處理,為了保證代碼的健壯性,我們需要對(duì)異常做一些處理。

    捕獲執(zhí)行器錯(cuò)誤

    現(xiàn)在我們需要對(duì)執(zhí)行器進(jìn)行異常捕獲,如果發(fā)生異常,就將我們的狀態(tài)修改為rejected。

    捕獲執(zhí)行器的錯(cuò)誤也比較簡(jiǎn)單,只需要在構(gòu)造函數(shù)中加入try...catch語(yǔ)句就可以,

    實(shí)現(xiàn)代碼如下:

        constructor(executor) {
            try {
                // 在調(diào)用executor需要傳遞兩個(gè)回調(diào)函數(shù),分別是resolve和reject
                executor(this.resolve, this.reject)
            } catch (e) {
                // 發(fā)生異常調(diào)用reject
                this.reject(e)
            }
        }

    測(cè)試代碼如下:

    const MyPromise = require('./myPromise')
    let promise = new MyPromise((resolve, reject) => {
        throw new Error('執(zhí)行器錯(cuò)誤')
    })
    
    promise.then(value => {
        console.log(value);
    }, error => {
        console.log(error.message);
    })
    /* 輸出
        執(zhí)行器錯(cuò)誤
    */

    捕獲then中的報(bào)錯(cuò)

    現(xiàn)在我們需要對(duì)then中的異常捕獲到,并在下一次鏈?zhǔn)秸{(diào)用中傳遞到then的第二個(gè)函數(shù)中,實(shí)現(xiàn)的方式也是通過(guò)try...catch語(yǔ)句,

    示例代碼如下:

    // then方法的實(shí)現(xiàn)
    then (onFulfilled, onRejected) {
        // then 方法返回一個(gè)MyPromise實(shí)例
        const promise = new MyPromise((resolve, reject) => {
            // 判斷當(dāng)前狀態(tài),根據(jù)狀態(tài)調(diào)用指定回調(diào)
            if (this.status === FULFILLED) {
                // 這里并不需要延遲執(zhí)行,而是通過(guò)setTimeout將其變成異步函數(shù)
                // 如果不變成異步的話是在函數(shù)內(nèi)獲取不到promise的
                setTimeout(() => {
                    try {
                        // 將成功的值作為參數(shù)返回
                        // 保存執(zhí)行回調(diào)函數(shù)的結(jié)果
                        const result = onFulfilled(this.value)
    
                        // 如果返回的是一個(gè)普通的值,直接調(diào)用resolve
                        // 如果是一個(gè)MyPromise實(shí)例,根據(jù)返回的解決來(lái)決定是調(diào)用resolve,還是reject
                        resolvePromise(promise, result, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                }, 0)
            } else if (this.status === REJECTED) {
                // 將失敗的原因作為參數(shù)返回
                onRejected(this.reason)
            } else {
                // 表示既不是成功,也不是失敗。這個(gè)時(shí)候保存?zhèn)鬟f進(jìn)來(lái)的兩個(gè)回調(diào)
                this.onFulfilled.push(onFulfilled)
                this.onRejected.push(onRejected)
            }
        })
        return promise
    }

    測(cè)試代碼如下:

    const MyPromise = require('./myPromise')
    let promise = new MyPromise((resolve, reject) => {
        resolve('成功')
    })
    // 第一個(gè)then方法中的錯(cuò)誤要在第二個(gè)then方法中捕獲到
    promise.then(value => {
        console.log('resolve', value)
        throw new Error('then的執(zhí)行過(guò)程中遇到異常')
    }).then(null, reason => {
        console.log(reason.message)
    })
    
    /* 輸出
        resolve 成功
        then的執(zhí)行過(guò)程中遇到異常
    */

    錯(cuò)誤與異步狀態(tài)的鏈?zhǔn)秸{(diào)用

    現(xiàn)在只對(duì)成功狀態(tài)的then進(jìn)行的鏈?zhǔn)秸{(diào)用以及錯(cuò)誤處理,錯(cuò)誤與異步狀態(tài)未進(jìn)行處理,其實(shí)處理起來(lái)也是一樣的,

    示例代碼如下:

        // then方法的實(shí)現(xiàn)
        then (onFulfilled, onRejected) {
            // then 方法返回一個(gè)MyPromise實(shí)例
            const promise = new MyPromise((resolve, reject) => {
                // 判斷當(dāng)前狀態(tài),根據(jù)狀態(tài)調(diào)用指定回調(diào)
                if (this.status === FULFILLED) {
                    // 這里并不需要延遲執(zhí)行,而是通過(guò)setTimeout將其變成異步函數(shù)
                    // 如果不變成異步的話是在函數(shù)內(nèi)獲取不到promise的
                    setTimeout(() => {
                        try {
                            // 將成功的值作為參數(shù)返回
                            // 保存執(zhí)行回調(diào)函數(shù)的結(jié)果
                            const result = onFulfilled(this.value)
    
                            // 如果返回的是一個(gè)普通的值,直接調(diào)用resolve
                            // 如果是一個(gè)MyPromise實(shí)例,根據(jù)返回的解決來(lái)決定是調(diào)用resolve,還是reject
                            resolvePromise(promise, result, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)
                } else if (this.status === REJECTED) {
                    // 失敗的處理同成功處理,只是調(diào)用的回調(diào)函數(shù)不同
                    setTimeout(() => {
                        try {
                            const result = onRejected(this.reason)
                            resolvePromise(promise, result, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)
                } else {
                    this.onFulfilled.push((value) => {
                        setTimeout(() => {
                            try {
                                const result = onFulfilled(value)
                                resolvePromise(promise, result, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        }, 0)
                    })
                    this.onRejected.push((reason) => {
                        setTimeout(() => {
                            try {
                                const result = onRejected(reason)
                                resolvePromise(promise, result, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        }, 0)
                    })
                }
            })
            return promise
        }

    測(cè)試代碼如下:

    const MyPromise = require('./myPromise')
    let promise = new MyPromise((resolve, reject) => {
        setTimeout(resolve, 2000, '成功')
    })
    // 第一個(gè)then方法中的錯(cuò)誤要在第二個(gè)then方法中捕獲到
    promise.then(value => {
        console.log('resolve', value)
        throw new Error('then的執(zhí)行過(guò)程中遇到異常')
    }).then(null, reason => {
        console.log(reason.message)
    })
    /* 輸出
        resolve 成功
        then的執(zhí)行過(guò)程中遇到異常
    */

    將then方法的參數(shù)變成可選參數(shù)

    Promise中的then方法其實(shí)是兩個(gè)可以可選參數(shù),如果我們不傳遞任何參數(shù)的話,里面的結(jié)果是向下傳遞的,直到捕獲為止,

    例如下面這段代碼:

    new Promise((resolve, reject) => {
        resolve(100)
    })
        .then()
        .then()
        .then()
        .then(value => console.log(value))
    // 最后一個(gè)then輸入100

    這段代碼可以理解為:

    new Promise((resolve, reject) => {
        resolve(100)
    })
        .then(value => value)
        .then(value => value)
        .then(value => value)
        .then(value => console.log(value))

    所以說(shuō)我們只需要在沒(méi)有傳遞回調(diào)函數(shù)時(shí),賦值一個(gè)默認(rèn)的回調(diào)函數(shù)即可。

    實(shí)現(xiàn)代碼如下:

    // then方法的實(shí)現(xiàn)
    then (onFulfilled, onRejected) {
        // 如果傳遞函數(shù),就是用傳遞的函數(shù),否則指定一個(gè)默認(rèn)值,用于參數(shù)傳遞
        onFulfilled = onFulfilled ? onFulfilled : value => value
        // 同理
        onRejected = onRejected ? onRejected : reason => { throw reason }
        // then 方法返回一個(gè)MyPromise實(shí)例
        const promise = new MyPromise((resolve, reject) => {
            // 判斷當(dāng)前狀態(tài),根據(jù)狀態(tài)調(diào)用指定回調(diào)
            if (this.status === FULFILLED) {...
            } else if (this.status === REJECTED) {...
            } else {...
            }
        })
        return promise
    }

    Promise.all方法的實(shí)現(xiàn)

    關(guān)于all()方法的使用,可以參數(shù)Promise.all()。簡(jiǎn)單的說(shuō)Promise.all()會(huì)將多個(gè)Promise實(shí)例包裝為一個(gè)Promise實(shí)例,且順序與調(diào)用順序一致,

    示例代碼如下:

    function p1 () {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('p1')
            }, 2000)
        })
    }
    function p2 () {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('p2')
            }, 0)
        })
    }
    Promise.all(['a', 'b', p1(), p2(), 'c']).then(result => {
        console.log(result)
        // ["a", "b", "p1", "p2", "c"]
    })

    在這段代碼中,我們的p1的執(zhí)行是延遲了2s的,這里如果不使用Promise.all()的話最終順序是與我們調(diào)用不同的。

    現(xiàn)在我們來(lái)分析一下all()的實(shí)現(xiàn)思路:

    • all()方法可以通過(guò)類(lèi)直接調(diào)用,所以是一個(gè)靜態(tài)方法

    • all()方法接收一個(gè)數(shù)組,數(shù)組中的值可以是一個(gè)普通值,也可以是一個(gè)MyPromise的實(shí)例對(duì)象

    • return一個(gè)新的MyPromise實(shí)例對(duì)象

    • 遍歷數(shù)組中的每一個(gè)值,判斷值得類(lèi)型,如果是一個(gè)普通值得話直接將值存入一個(gè)數(shù)組;如果是一個(gè)MyPromise的實(shí)例對(duì)象的話,會(huì)調(diào)用then方法,然后根據(jù)執(zhí)行后的狀態(tài),如果失敗的話調(diào)用新的MyPromise實(shí)例對(duì)象中的rejecte,如果是成功話將這個(gè)值存入一個(gè)數(shù)組

    • 存入數(shù)組時(shí)計(jì)數(shù),如果存入的數(shù)量達(dá)到傳入的數(shù)組長(zhǎng)度,說(shuō)明調(diào)用完畢,執(zhí)行resolve并將最終的結(jié)果數(shù)組作為參數(shù)返回。

    實(shí)現(xiàn)代碼:

    /** 
     * @description: 將多個(gè)Promise實(shí)例合并為一個(gè)Promise實(shí)例
     * @param {*} array Promise或者普通值
     * @returns {Promise}
     */
    static all (array) {
        // 用于存放最終結(jié)果的數(shù)組
        let result = []
        // 用于計(jì)算當(dāng)前已經(jīng)執(zhí)行完的實(shí)例的數(shù)量
        let count = 0
        // 最后返回的是一個(gè)Promise實(shí)例
        return new MyPromise((resolve, reject) => {
            /** 
             * @description: 將執(zhí)行完畢的值加入結(jié)果數(shù)組,并根據(jù)實(shí)際情況決定是否調(diào)用resolve
             * @param {*} result 存放結(jié)果的數(shù)組
             * @param {*} index 要加入的索引
             * @param {*} value 要加入數(shù)組的值
             * @param {*} resolve Promise中的resolve
             */
            function addResult (result, index, value, resolve) {
                // 根據(jù)索引值,將結(jié)果堆入數(shù)組中
                result[index] = value
                // 執(zhí)行完畢一個(gè) count+1,如果當(dāng)前值等于總長(zhǎng)度的話說(shuō)明已經(jīng)執(zhí)行結(jié)束了,可以直接調(diào)用resolve,說(shuō)明已經(jīng)成功執(zhí)行完畢了
                if (++count === array.length) {
                    // 將執(zhí)行結(jié)果返回
                    resolve(result)
                }
            }
            // 遍歷穿入的數(shù)組,每個(gè)都執(zhí)行then方法,獲取到最終的結(jié)果
            array.forEach((p, index) => {
                // 判斷p是不是MyPromise的實(shí)例,如果是的話調(diào)用then方法,不是直接將值加入數(shù)組中
                if (p instanceof MyPromise) {
                    p.then(
                        // 成功時(shí)將結(jié)果直接加入數(shù)組中
                        value => {
                            addResult(result, index, value, resolve)
                        },
                        // 如果失敗直接返回失敗原因
                        reason => {
                            reject(reason)
                        }
                    )
                }
                else {
                    addResult(result, index, p, resolve)
                }
            })
        })
    }

    Promise.resolve方法的實(shí)現(xiàn)

    關(guān)于Promise.resolve()方法的用法可以參考Promise.resolve()Promise.reject()

    我們實(shí)現(xiàn)的思路主要如下:

    • 該方法是一個(gè)靜態(tài)方法

    • 該方法接受的如果是一個(gè)值就將該值包裝為一個(gè)MyPromise的實(shí)例對(duì)象返回,如果是一個(gè)MyPromise的實(shí)例對(duì)象,調(diào)用then方法返回。

    實(shí)現(xiàn)代碼如下:

    static resolve (value) {
        // 如果是MyPromise的實(shí)例,就直接返回這個(gè)實(shí)例
        if (value instanceof MyPromise) return value
        // 如果不是的話創(chuàng)建一個(gè)MyPromise實(shí)例,并返回傳遞的值
        return new MyPromise((resolve) => {
            resolve(value)
        })
    }

    finally方法的實(shí)現(xiàn)

    關(guān)于finally方法可參考finally()實(shí)現(xiàn)該方法的實(shí)現(xiàn)代碼如下:

    finally (callback) {
        // 如何拿到當(dāng)前的promise的狀態(tài),使用then方法,而且不管怎樣都返回callback
        // 而且then方法就是返回一個(gè)promise對(duì)象,那么我們直接返回then方法調(diào)用之后的結(jié)果即可
        // 我們需要在回調(diào)之后拿到成功的回調(diào),所以需要把value也return
        // 失敗的回調(diào)也拋出原因
        // 如果callback是一個(gè)異步的promise對(duì)象,我們還需要等待其執(zhí)行完畢,所以需要用到靜態(tài)方法resolve
        return this.then(value => {
            // 把callback調(diào)用之后返回的promise傳遞過(guò)去,并且執(zhí)行promise,且在成功之后返回value
            return MyPromise.resolve(callback()).then(() => value)
        }, reason => {
            // 失敗之后調(diào)用的then方法,然后把失敗的原因返回出去。
            return MyPromise.resolve(callback()).then(() => { throw reason })
        })
    }

    測(cè)試:

    function p1 () {
        return new MyPromise((resolve, reject) => {
            setTimeout(() => {
                resolve('p1')
            }, 2000)
        })
    }
    function p2 () {
        return new MyPromise((resolve, reject) => {
            reject('p2 reject')
        })
    }
    p2().finally(
        () => {
            console.log('finally p2')
            return p1()
        }
    ).then(
        value => {
            console.log(value)
        }, reason => {
            console.log(reason)
        }
    )
    // finally p2
    // 兩秒之后執(zhí)行p2 reject

    catch方法的實(shí)現(xiàn)

    關(guān)于catch方法可以參考catch(),實(shí)現(xiàn)該方法其實(shí)非常簡(jiǎn)單,只需要在內(nèi)部調(diào)用then方法,不傳遞第一個(gè)回調(diào)函數(shù)即可,

    實(shí)現(xiàn)代碼如下:

    catch (callback) {
        return this.then(null, failCallback)
    }

    測(cè)試如下:

    function p () {
        return new MyPromise((resolve, reject) => {
            reject(new Error('reject'))
        })
    }
    p()
        .then(value => {
            console.log(value)
        })
        .catch(reason => console.log(reason))

    完整代碼

    // MyPromise.js
    // 定義所有狀態(tài)的常量
    const PENDING = 'pending'
    const FULFILLED = 'fulfilled'
    const REJECTED = 'rejected'
    // Promise實(shí)質(zhì)上就是一個(gè)類(lèi),首先創(chuàng)建一個(gè)Promise的類(lèi)
    class MyPromise {
        // 實(shí)例化Promise時(shí)需要一個(gè)回調(diào)函數(shù),該回調(diào)函數(shù)立即執(zhí)行
        constructor(executor) {
            try {
                // 在調(diào)用executor需要傳遞兩個(gè)回調(diào)函數(shù),分別是resolve和reject
                executor(this.resolve, this.reject)
            } catch (e) {
                // 發(fā)生異常調(diào)用reject
                this.reject(e)
            }
        }
        // Promise 的狀態(tài)
        status = PENDING
        // 記錄成功與失敗的值
        value = undefined
        reason = undefined
        // 存儲(chǔ)成功與失敗的處理邏輯
        onFulfilled = []
        onRejected = []
    
        resolve = (value) => {// 這里使用箭頭函數(shù)是為了使其內(nèi)部的this指向?yàn)樵搶?shí)例化后的對(duì)象
            // 形參value表示,調(diào)用resolve時(shí)傳遞的參數(shù)
            // 如果當(dāng)前狀態(tài)不是pending,就直接跳出該邏輯
            if (this.status !== PENDING) return
            // 將狀態(tài)修改為成功
            this.status = FULFILLED
            // 將傳入的值進(jìn)行保存
            this.value = value
            // 如果將狀態(tài)修復(fù)為成功,調(diào)用成功的回調(diào)
            // this.onFulfilled && this.onFulfilled(this.value)
            while (this.onFulfilled.length) {
                // Array.prototype.shift() 用于刪除數(shù)組第一個(gè)元素,并返回
                this.onFulfilled.shift()(this.value)
            }
        }
        reject = (reason) => {// 這里使用箭頭函數(shù)是為了使其內(nèi)部的this指向?yàn)樵搶?shí)例化后的對(duì)象
            // 形參reason表示,調(diào)用reject時(shí)傳遞的失敗的原因
            // 如果當(dāng)前狀態(tài)不是pending,就直接跳出該邏輯
            if (this.status !== PENDING) return
    
            // 將狀態(tài)修改為失敗
            this.status = REJECTED
    
            // 保存失敗的原因
            this.reason = reason
    
            // 如果將狀態(tài)修復(fù)為失敗,調(diào)用失敗的回調(diào)
            // this.onRejected && this.onRejected(this.reason)
            while (this.onRejected.length) {
                // Array.prototype.shift() 用于刪除數(shù)組第一個(gè)元素,并返回
                this.onRejected.shift()(this.reason)
            }
        }
        // then方法的實(shí)現(xiàn)
        then (onFulfilled, onRejected) {
            // 如果傳遞函數(shù),就是用傳遞的函數(shù),否則指定一個(gè)默認(rèn)值,用于參數(shù)傳遞
            onFulfilled = onFulfilled ? onFulfilled : value => value
            // 同理
            onRejected = onRejected ? onRejected : reason => { throw reason }
            // then 方法返回一個(gè)MyPromise實(shí)例
            const promise = new MyPromise((resolve, reject) => {
                // 判斷當(dāng)前狀態(tài),根據(jù)狀態(tài)調(diào)用指定回調(diào)
                if (this.status === FULFILLED) {
                    // 這里并不需要延遲執(zhí)行,而是通過(guò)setTimeout將其變成異步函數(shù)
                    // 如果不變成異步的話是在函數(shù)內(nèi)獲取不到promise的
                    setTimeout(() => {
                        try {
                            // 將成功的值作為參數(shù)返回
                            // 保存執(zhí)行回調(diào)函數(shù)的結(jié)果
                            const result = onFulfilled(this.value)
    
                            // 如果返回的是一個(gè)普通的值,直接調(diào)用resolve
                            // 如果是一個(gè)MyPromise實(shí)例,根據(jù)返回的解決來(lái)決定是調(diào)用resolve,還是reject
                            resolvePromise(promise, result, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)
                } else if (this.status === REJECTED) {
                    // 失敗的處理同成功處理,只是調(diào)用的回調(diào)函數(shù)不同
                    setTimeout(() => {
                        try {
                            const result = onRejected(this.reason)
                            resolvePromise(promise, result, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)
                } else {
                    // 表示既不是成功,也不是失敗。這個(gè)時(shí)候保存?zhèn)鬟f進(jìn)來(lái)的兩個(gè)回調(diào)
                    // this.onFulfilled.push(onFulfilled)
                    // this.onRejected.push(onRejected)
                    this.onFulfilled.push((value) => {
                        setTimeout(() => {
                            try {
                                const result = onFulfilled(value)
                                resolvePromise(promise, result, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        }, 0)
                    })
                    this.onRejected.push((reason) => {
                        setTimeout(() => {
                            try {
                                const result = onRejected(reason)
                                resolvePromise(promise, result, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        }, 0)
                    })
                }
            })
            return promise
        }
        catch (callback) {
            return this.then(null, callback)
        }
        finally (callback) {
            // 如何拿到當(dāng)前的promise的狀態(tài),使用then方法,而且不管怎樣都返回callback
            // 而且then方法就是返回一個(gè)promise對(duì)象,那么我們直接返回then方法調(diào)用之后的結(jié)果即可
            // 我們需要在回調(diào)之后拿到成功的回調(diào),所以需要把value也return
            // 失敗的回調(diào)也拋出原因
            // 如果callback是一個(gè)異步的promise對(duì)象,我們還需要等待其執(zhí)行完畢,所以需要用到靜態(tài)方法resolve
            return this.then(value => {
                // 把callback調(diào)用之后返回的promise傳遞過(guò)去,并且執(zhí)行promise,且在成功之后返回value
                return MyPromise.resolve(callback()).then(() => value)
            }, reason => {
                // 失敗之后調(diào)用的then方法,然后把失敗的原因返回出去。
                return MyPromise.resolve(callback()).then(() => { throw reason })
            })
        }
        /** 
         * @description: 將多個(gè)Promise實(shí)例合并為一個(gè)Promise實(shí)例
         * @param {*} array Promise或者普通值
         * @returns {Promise}
         */
        static all (array) {
            // 用于存放最終結(jié)果的數(shù)組
            let result = []
            // 用于計(jì)算當(dāng)前已經(jīng)執(zhí)行完的實(shí)例的數(shù)量
            let count = 0
            // 最后返回的是一個(gè)Promise實(shí)例
            return new MyPromise((resolve, reject) => {
                /** 
                 * @description: 將執(zhí)行完畢的值加入結(jié)果數(shù)組,并根據(jù)實(shí)際情況決定是否調(diào)用resolve
                 * @param {*} result 存放結(jié)果的數(shù)組
                 * @param {*} index 要加入的索引
                 * @param {*} value 要加入數(shù)組的值
                 * @param {*} resolve Promise中的resolve
                 */
                function addResult (result, index, value, resolve) {
                    // 根據(jù)索引值,將結(jié)果堆入數(shù)組中
                    result[index] = value
                    // 執(zhí)行完畢一個(gè) count+1,如果當(dāng)前值等于總長(zhǎng)度的話說(shuō)明已經(jīng)執(zhí)行結(jié)束了,可以直接調(diào)用resolve,說(shuō)明已經(jīng)成功執(zhí)行完畢了
                    if (++count === array.length) {
                        // 將執(zhí)行結(jié)果返回
                        resolve(result)
                    }
                }
                // 遍歷穿入的數(shù)組,每個(gè)都執(zhí)行then方法,獲取到最終的結(jié)果
                array.forEach((p, index) => {
                    // 判斷p是不是MyPromise的實(shí)例,如果是的話調(diào)用then方法,不是直接將值加入數(shù)組中
                    if (p instanceof MyPromise) {
                        p.then(
                            // 成功時(shí)將結(jié)果直接加入數(shù)組中
                            value => {
                                addResult(result, index, value, resolve)
                            },
                            // 如果失敗直接返回失敗原因
                            reason => {
                                reject(reason)
                            }
                        )
                    }
                    else {
                        addResult(result, index, p, resolve)
                    }
                })
            })
        }
        static resolve (value) {
            // 如果是MyPromise的實(shí)例,就直接返回這個(gè)實(shí)例
            if (value instanceof MyPromise) return value
            // 如果不是的話創(chuàng)建一個(gè)MyPromise實(shí)例,并返回傳遞的值
            return new MyPromise((resolve) => {
                resolve(value)
            })
        }
    
    }
    function resolvePromise (promise, result, resolve, reject) {
        // 這里修改一下該函數(shù),如果return的Promise實(shí)例對(duì)象,也就是傳入的promise===result的話,說(shuō)明在promise中return的是當(dāng)前promise對(duì)象。
        if (promise === result) {
            // 這里調(diào)用reject,并拋出一個(gè)Error
            // return 是必須的,阻止程序向下執(zhí)行
            return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
        }
        // 判斷傳遞的result是不是MyPromise的實(shí)例對(duì)象
        if (result instanceof MyPromise) {
            // 說(shuō)明是MyPromise的實(shí)例對(duì)象
            // 調(diào)用.then方法,然后在回調(diào)函數(shù)中獲取到具體的值,然后調(diào)用具體的回調(diào)
            // result.then(value => resolve(value), reason => reject(reason))
            // 簡(jiǎn)寫(xiě)
            result.then(resolve, reject)
        } else {
            resolve(result)
        }
    }
    module.exports = MyPromise

    關(guān)于“怎么使用JavaScript手寫(xiě)一個(gè)Promise”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí),可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會(huì)為大家更新不同的知識(shí)點(diǎn)。

    向AI問(wèn)一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI