成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

從零開始寫一個 Promise 庫

Binguner / 1029人閱讀

摘要:是什么在規(guī)范中,是一個類,它的構造函數(shù)接受一個函數(shù)。在這種情況下,是但處于狀態(tài)。與一起使用關鍵字會暫停執(zhí)行一個函數(shù),直到等待的變成狀態(tài)。此外,會一直等待調(diào)用直到下一個時序。

原文:Write Your Own Node.js Promise Library from Scratch

作者:code_barbarian

Promise 已經(jīng)是 JavaScript 中異步處理的基石,回調(diào)的場景將會越來越少,而且現(xiàn)在可以直接在 Node.js 使用 async/await。async/await 基于 Promise,因此需要了解 Promise 來掌握 async/await。這篇文章,將介紹如何編寫一個 Promise 庫,并演示如何使用 async/await。

Promise 是什么?

在 ES6 規(guī)范中,Promise 是一個類,它的構造函數(shù)接受一個 executor 函數(shù)。Promise 類的實例有一個 then() 方法。根據(jù)規(guī)范,Promise 還有其他的一些屬性,但在這里可以暫時忽略,因為我們要實現(xiàn)的是一個精簡版的庫。下面是一個 MyPromise 類的腳手架:

class MyPromise {
    // `executor` 函數(shù)接受兩個參數(shù),`resolve()` 和 `reject()`
    // 負責在異步操作成功(resolved)或者失敗(rejected)的時候調(diào)用 `resolve()` 或者 `reject()`
    constructor(executor) {}

    // 當 promise 的狀態(tài)是 fulfilled(完成)時調(diào)用 `onFulfilled` 方法,
    // 當 promise 的狀態(tài)是 rejected(失敗)時調(diào)用 `onRejected` 方法
    // 到目前為止,可以認為 "fulfilled" 和 "resolved" 是一樣的
    then(onFulfilled, onRejected) {}
}

executor 函數(shù)需要兩個參數(shù),resolve()reject()。promise 是一個狀態(tài)機,包含三個狀態(tài):

pending:初始狀態(tài),既不是成功,也不是失敗狀態(tài)

fulfilled:意味著操作成功完成,返回結(jié)果值

rejected:意味著操作失敗,返回錯誤信息

這樣很容易就能實現(xiàn) MyPromise 構造函數(shù)的初始版本:

constructor(executor) {
    if (typeof executor !== "function") {
        throw new Error("Executor must be a function")
    }

    // 初始狀態(tài),$state 表示 promise 的當前狀態(tài)
    // $chained 是當 promise 處在 settled 狀態(tài)時需要調(diào)用的函數(shù)數(shù)組
    this.$state = "PENDING"
    this.$chained = []

    // 為處理器函數(shù)實現(xiàn) `resolve()` 和 `reject()`
    const resolve = res => {
        // 只要當 `resolve()` 或 `reject()` 被調(diào)用
        // 這個 promise 對象就不再處于 pending 狀態(tài),被稱為 settled 狀態(tài)
        // 調(diào)用 `resolve()` 或 `reject()` 兩次,以及在 `resolve()` 之后調(diào)用 `reject()` 是無效的
        if (this.$state !== "PENDING") {
            return
        }
        // 后面將會談到 fulfilled 和 resolved 之間存在細微差別
        this.$state = "FULFILLED"
        this.$internalValue = res
        // If somebody called `.then()` while this promise was pending, need
        // to call their `onFulfilled()` function
        for (const { onFulfilled } of this.$chained) {
            onFulfilled(res)
        }
    }
    const reject = err => {
        if (this.$state !== "PENDING") {
            return
        }
        this.$state = "REJECTED"
        this.$internalValue = err
        for (const { onRejected } of this.$chained) {
            onRejected(err)
        }
    }

    // 如規(guī)范所言,調(diào)用處理器函數(shù)中的 `resolve()` 和 `reject()`
    try {
        // 如果處理器函數(shù)拋出一個同步錯誤,我們認為這是一個失敗狀態(tài)
        // 需要注意的是,`resolve()` 和 `reject()` 只能被調(diào)用一次
        executor(resolve, reject)
    } catch (err) {
        reject(err)
    }
}

then() 函數(shù)的實現(xiàn)更簡單,它接受兩個參數(shù),onFulfilled()onRejected()then() 函數(shù)必須確保 promise 在 fulfilled 時調(diào)用 onFulfilled(),在 rejected 時調(diào)用 onRejected()。如果 promise 已經(jīng) resolved 或 rejected,then() 函數(shù)會立即調(diào)用 onFulfilled()onRejected()。如果 promise 仍處于 pending 狀態(tài),就將函數(shù)推入 $chained 數(shù)組,因此后續(xù) resolve()reject() 函數(shù)仍然可以調(diào)用它們。

then(onFulfilled, onRejected) {
    if (this.$state === "FULFILLED") {
        onFulfilled(this.$internalValue)
    } else if (this.$state === "REJECTED") {
        onRejected(this.$internalValue)
    } else {
        this.$chained.push({ onFulfilled, onRejected })
    }
}

*除此之外:ES6 規(guī)范表示,如果在已經(jīng) resolved 或 rejected 的 promise 調(diào)用 .then(), 那么 onFulfilled()onRejected() 將在下一個時序被調(diào)用。由于本文代碼只是一個教學示例而不是規(guī)范的精確實現(xiàn),因此實現(xiàn)會忽略這些細節(jié)。

Promise 調(diào)用鏈

上面的例子特意忽略了 promise 中最復雜也是最有用的部分:鏈式調(diào)用。如果 onFulfilled() 或者 onRejected() 函數(shù)返回一個 promise,則 then() 應該返回一個 “l(fā)ocked in” 的新 promise 以匹配這個 promise 的狀態(tài)。例如:

p = new MyPromise(resolve => {
    setTimeout(() => resolve("World"), 100)
})

p
    .then(res => new MyPromise(resolve => resolve(`Hello, ${res}`)))
    // 在 100 ms 后打印 "Hello, World"
    .then(res => console.log(res))

下面是可以返回 promise 的 .then() 函數(shù)實現(xiàn),這樣就可以進行鏈式調(diào)用。

then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
        // 確保在 `onFulfilled()` 和 `onRejected()` 的錯誤將導致返回的 promise 失?。╮eject)
        const _onFulfilled = res => {
            try {
                // 如果 `onFulfilled()` 返回一個 promise, 確保 `resolve()` 能正確處理
                resolve(onFulfilled(res))
            } catch (err) {
                reject(err)
            }
        }
        const _onRejected = err => {
            try {
                reject(onRejected(err))
            } catch (_err) {
                reject(_err)
            }
        }
        if (this.$state === "FULFILLED") {
            _onFulfilled(this.$internalValue)
        } else if (this.$state === "REJECTED") {
            _onRejected(this.$internalValue)
        } else {
            this.$chained.push({ onFulfilled: _onFulfilled, onRejected: _onRejected })
        }
    })
}

現(xiàn)在 then() 返回一個 promise,但是還需要完成一些工作:如果 onFulfilled() 返回一個 promise,resolve() 要能夠正確處理。所以 resolve() 函數(shù)需要在 then() 遞歸調(diào)用,下面是更新后的 resolve() 函數(shù):

const resolve = res => {
    // 只要當 `resolve()` 或 `reject()` 被調(diào)用
    // 這個 promise 對象就不再處于 pending 狀態(tài),被稱為 settled 狀態(tài)
    // 調(diào)用 `resolve()` 或 `reject()` 兩次,以及在 `resolve()` 之后調(diào)用 `reject()` 是無效的
    if (this.$state !== "PENDING") {
        return
    }

    // 如果 `res` 是 thenable(帶有then方法的對象)
    // 將鎖定 promise 來保持跟 thenable 的狀態(tài)一致
    if (res !== null && typeof res.then === "function") {
        // 在這種情況下,這個 promise 是 resolved,但是仍處于 "PENDING" 狀態(tài)
        // 這就是 ES6 規(guī)范中說的"一個 resolved 的 promise",可能處在 pending, fulfilled 或者 rejected 狀態(tài)
        // http://www.ecma-international.org/ecma-262/6.0/#sec-promise-objects
        return res.then(resolve, reject)
    }

    this.$state = "FULFILLED"
    this.$internalValue = res
    // If somebody called `.then()` while this promise was pending, need
    // to call their `onFulfilled()` function
    for (const { onFulfilled } of this.$chained) {
        onFulfilled(res)
    }

    return res
}

為了簡單起見,上面的例子省略了一旦 promise 被鎖定用以匹配另一個 promise 時,調(diào)用 resolve() 或者 reject() 是無效的關鍵細節(jié)。在上面的例子中,你可以 resolve() 一個 pending 的 promise ,然后拋出一個錯誤,然后 res.then(resolve, reject) 將會無效。這僅僅是一個例子,而不是 ES6 promise 規(guī)范的完全實現(xiàn)。

上面的代碼說明了 resolved 的 promise 和 fulfilled 的 promise 之間的區(qū)別。這種區(qū)別是微妙的,并且與 promise 鏈式調(diào)用有關。resolved 不是一種真正的 promise 狀態(tài),但它是ES6規(guī)范中定義的術語。當對一個已經(jīng) resolved 的 promise 調(diào)用 resolve(),可能會發(fā)生以下兩件事之一:

在調(diào)用 resolve(v)時,如果 v 不是一個 promise ,那么 promise 立即成為 fulfilled。在這種簡單的情況下,resolved 和 fulfilled 就是一樣的。

在調(diào)用 resolve(v)時,如果 v 是另一個 promise,那么這個 promise 一直處于 pending 直到 v 調(diào)用 resolve 或者 reject。在這種情況下, promise 是 resolved 但處于 pending 狀態(tài)。

與 Async/Await 一起使用

關鍵字 await 會暫停執(zhí)行一個 async 函數(shù),直到等待的 promise 變成 settled 狀態(tài)?,F(xiàn)在我們已經(jīng)有了一個簡單的自制 promise 庫,看看結(jié)合使用 async/await 中時會發(fā)生什么。向 then() 函數(shù)添加一個 console.log() 語句:

then(onFulfilled, onRejected) {
    console.log("Then", onFulfilled, onRejected, new Error().stack)
    return new MyPromise((resolve, reject) => {
        /* ... */
    })
}

現(xiàn)在,我們來 await 一個 MyPromise 的實例,看看會發(fā)生什么。

run().catch(error => console.error(error.stack))

async function run() {
    const start = Date.now()
    await new MyPromise(resolve => setTimeout(() => resolve(), 100))
    console.log("Elapsed time", Date.now() - start)
}

注意上面的 .catch() 調(diào)用。catch() 函數(shù)是 ES6 promise 規(guī)范的核心部分。本文不會詳細講述它,因為 .catch(f) 相當于 .then(null, f),沒有什么特別的內(nèi)容。

以下是輸出內(nèi)容,注意 await 隱式調(diào)用 .then() 中的 onFulfilled()onRejected() 函數(shù),這是 V8 底層的 C++ 代碼(native code)。此外,await 會一直等待調(diào)用 .then() 直到下一個時序。

Then function () { [native code] } function () { [native code] } Error
    at MyPromise.then (/home/val/test/promise.js:63:50)
    at process._tickCallback (internal/process/next_tick.js:188:7)
    at Function.Module.runMain (module.js:686:11)
    at startup (bootstrap_node.js:187:16)
    at bootstrap_node.js:608:3
Elapsed time 102
更多

async/await 是非常強大的特性,但掌握起來稍微有點困難,因為需要使用者了解 promise 的基本原則。 promise 有很多細節(jié),例如捕獲處理器函數(shù)中的同步錯誤,以及 promise 一旦解決就無法改變狀態(tài),這使得 async/await 成為可能。一旦對 promise 有了充分的理解,async/await 就會變得容易得多。

文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/94257.html

相關文章

  • 從零開始實現(xiàn)一個自己的Promise

    摘要:所以,這篇文章我會帶大家從零開始,手寫一個基本能用的。首先,規(guī)定對象是一個構造函數(shù),用來生成實例。然后,這個構造函數(shù)接受一個函數(shù)作為參數(shù),該函數(shù)的兩個參數(shù)分別是和。對象通過自身的狀態(tài),來控制異步操作。 剛開始寫前端的時候,處理異步請求經(jīng)常用callback,簡單又順手。后來寫著寫著就拋棄了callback,開始用promise來處理異步問題。promise寫起來確實更加優(yōu)美,但由于缺乏...

    paulquei 評論0 收藏0
  • 前端之從零開始系列

    摘要:只有動手,你才能真的理解作者的構思的巧妙只有動手,你才能真正掌握一門技術持續(xù)更新中項目地址求求求源碼系列跟一起學如何寫函數(shù)庫中高級前端面試手寫代碼無敵秘籍如何用不到行代碼寫一款屬于自己的類庫原理講解實現(xiàn)一個對象遵循規(guī)范實戰(zhàn)手摸手,帶你用擼 Do it yourself!!! 只有動手,你才能真的理解作者的構思的巧妙 只有動手,你才能真正掌握一門技術 持續(xù)更新中…… 項目地址 https...

    Youngdze 評論0 收藏0
  • JavaScript 異步

    摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復雜性。寫一個符合規(guī)范并可配合使用的寫一個符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問題描述 在開發(fā)過程中,遇到一個需求:在系統(tǒng)初始化時通過http獲取一個第三方服務器端的列表,第三方服務器提供了一個接口,可通過...

    tuniutech 評論0 收藏0
  • 從零開始搭建React同構應用(二):瀏覽器端工程化

    摘要:從零開始搭建同構應用二項目工程化瀏覽器端在從零開始同構開發(fā)一中我們已經(jīng)能實現(xiàn)基本的配置和編譯了。接下來我們需要將編譯工作工程化。配置作用自動生成自動在引入,。文件內(nèi)容如下同構開發(fā)配置自動刷新這里我們用到的修飾器特性。 從零開始搭建React同構應用(二) 項目工程化(瀏覽器端) 在從零開始React同構開發(fā)(一)中我們已經(jīng)能實現(xiàn)基本的React配置和編譯了。接下來我們需要將編譯工作工程...

    wwq0327 評論0 收藏0
  • javascript知識點

    摘要:模塊化是隨著前端技術的發(fā)展,前端代碼爆炸式增長后,工程化所采取的必然措施。目前模塊化的思想分為和。特別指出,事件不等同于異步,回調(diào)也不等同于異步。將會討論安全的類型檢測惰性載入函數(shù)凍結(jié)對象定時器等話題。 Vue.js 前后端同構方案之準備篇——代碼優(yōu)化 目前 Vue.js 的火爆不亞于當初的 React,本人對寫代碼有潔癖,代碼也是藝術。此篇是準備篇,工欲善其事,必先利其器。我們先在代...

    Karrdy 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<