• <noscript id="ecgc0"><kbd id="ecgc0"></kbd></noscript>
    <menu id="ecgc0"></menu>
  • <tt id="ecgc0"></tt>

    ES6之async+await 同步/異步方案

    異步編程一直是JavaScript 編程的重大事項。關于異步方案, ES6 先是出現了 基于狀態管理的 Promise,然后出現了 Generator 函數 + co 函數,緊接著又出現了 ES7 的 async + await 方案。

    本文力求以最簡明的方式來疏通 async + await。

    工具/原料

    • JavaScript

    方法/步驟

    1. 1

      異步編程的幾個場景

      先從一個常見問題開始:一個for 循環中,如何異步的打印迭代順序

      用閉包或者ES6 規定的 let 塊級作用域來回答這個問題。

    2. 2

      這里描述的是一個均勻發生的的異步,被依次按既定的順序排在異步隊列中等待執行。

      如果異步不是均勻發生的,那么被注冊在異步隊列中的順序就是亂序的。

    3. 3

      返回的結果是亂序不可控的,這本來就是最為真實的異步。另一種情況是,在循環中,如果希望前一個異步執行完畢、后一個異步再執行,該怎么辦?

    4. 4

      這不就是多個異步 “串行” 

      在回調 callback 嵌套異步操作、再回調的方式,不就解決了這個問題!或者,使用 Promise + then() 層層嵌套同樣也能解決問題。但是,如果硬是要將這種嵌套的方式寫在循環中,還恐怕還需費一番周折。

      異步同步化方案

      如果要去將一批數據發送到服務器,只有前一批發送成功(即服務器返回成功的響應),才開始下一批數據的發送,否則終止發送。這就是一個典型的 “for 循環中存在相互依賴的異步操作” 的例子。

      明顯,這種 “串行” 的異步,實質上可以當成同步。它和亂序的異步比較起來,花費了更多的時間。按理說,我們希望程序異步執行,就是為了 “跳過” 阻塞,較少時間花銷。但與之相反的是,如果需要一系列的異步 “串行”,我們應該怎樣很好的進行編程?

      對于這個 “串行” 異步,有了 ES6 就非常容易的解決了這個問題。

    5. 5

      本次循環,等有了結果,再進行下一次循環。因此,循環每執行一次就會被暫停(“卡住”)一次,直到循環結束。這種編碼實現,很好的消除了層層嵌套的 “回調地獄” 問題,降低了認知難度。

      這就是異步問題同步化的方案。如果說 Promise 主要解決的是異步回調問題,那么 async + await 主要解決的就是將異步問題同步化,降低異步編程的認知負擔。

      async + await “外異內同”

      早先接觸這套 API 時,看著繁瑣的文檔,一知半解的認為 async + await 主要用來解決異步問題同步化的。

      其實不然。從上面的例子看到:async 關鍵字聲明了一個 異步函數,這個 異步函數 體內有一行 await 語句,它告示了該行為同步執行,并且與上下相鄰的代碼是依次逐行執行的。

      將這個形式化的東西再翻譯一下,就是:

      1、async 函數執行后,總是返回了一個 promise 對象2、await 所在的那一行語句是同步的

      其中,1 說明了從外部看,task 方法執行后返回一個 Promise 對象,正因為它返回的是 Promise,所以可以理解task 是一個異步方法。毫無疑問它是這樣用的:

    6. 6

      2說明了在 task 函數內部,異步已經被 “削” 成了同步。整個就是一個執行稍微耗時的函數而已。

      綜合 1、2,從形式上看,就是 “task 整體是一個異步函數,內部整個是同步的”,簡稱“外異內同”。

      整體是一個異步函數 不難理解。在實現上,我們不妨逆向一下,語言層面讓async關鍵字調用時,在函數執行的末尾強制增加一個promise 反回:

    7. 7

      實際上 await 調用,是讓后邊的語句(函數)做了一個遞歸執行,直到獲取到結果并使其 狀態 變更,才會 resolve 掉,而只有 resolve 掉,await 那一行代碼才算執行完,才繼續往下一行執行。所以,盡管外部是一個大大的 for 循環,但是整個 for 循環是依次串行的。

      因此,僅從上述框架的外觀出發,就不難理解 async + await 的意義。使用起來也就這么簡單,反而 Promise 是一個必須掌握的基礎件。

      秉承本次《重讀 ES6》系列的原則,不過多追求理解細節和具體實現過程。繼續鞏固一下這個 “形式化” 的理解。

      async + await 的進一步理解

      有這樣的一個異步操作 longTimeTask,已經用 Promise 進行了包裝。借助該函數進行一系列驗證。

    8. 8

      以上 2 步執行,清晰的證明了 exec1 函數體內是同步、逐行逐行執行的,即先執行完異步操作,然后進行 console.log() 打印。而 exec1() 的執行結果就直接是一個 Promise,因為它最先會蹦出來一串 Promise ...,然后才是 exec1 函數的內部執行日志。

      因此,所有驗證,完全符合 整體是一個異步函數,內部整個是同步的 的總結。

      await 如何執行其后語句

      回到 await ,看看它是如何執行其后邊的語句的。假設:讓 longTimeTask() 后邊直接帶 then() 回調,分兩種情況:

      1)then() 中不再返回任何東西2) then() 中繼續手動返回另一個 promise

    9. 9

      longTimeTask() 加上再多得 then() 回調,也不過是放在了它的回調列隊 queue 里了。也就是說,await 命令之后始終是一條 表達式語句,只不過上述代碼書寫方式比較讓人迷惑。(比較好的實踐建議是,將 longTimeTask 方法身后的 then() 移入 longTimeTask 函數體封裝起來)

      其次,手動返回另一個 promise 和什么也不返回,關系到 longTimeTask() 方法最終 resolve 出去的內容不一樣。換句話說,await 命令會提取其后邊的promise 的 resolve 結果,進而直接導致 result 的不同。

      值得強調的是,await 命令只認 resolve 結果,對 reject 結果報錯。不妨用以下的 return 語句替換上述 return 進行驗證。

    • 發表于 2018-02-05 00:00
    • 閱讀 ( 862 )
    • 分類:其他類型

    你可能感興趣的文章

    相關問題

    0 條評論

    請先 登錄 后評論
    admin
    admin

    0 篇文章

    作家榜 ?

    1. xiaonan123 189 文章
    2. 湯依妹兒 97 文章
    3. luogf229 46 文章
    4. jy02406749 45 文章
    5. 小凡 34 文章
    6. Daisy萌 32 文章
    7. 我的QQ3117863681 24 文章
    8. 華志健 23 文章

    聯系我們:uytrv@hotmail.com 問答工具
  • <noscript id="ecgc0"><kbd id="ecgc0"></kbd></noscript>
    <menu id="ecgc0"></menu>
  • <tt id="ecgc0"></tt>
    久久久久精品国产麻豆