【譯】Async-Await≈Generators+Promises

Async-Await ≈ Generators + Promisesjavascript

這篇文章我將介紹ES2017async函數爲何是ES2016GeneratorsPromises特性功能的語法糖。java

閱讀須知

  • 本文不對三者概念進行介紹和講解
  • 本文惟一的目的就是介紹如何利用GeneratorsPromises去實現async
  • 本文對async和其餘類似實現不進行優劣評價
  • 本文代碼都是通過巧妙設計以便於理解,他們不適用於實際開發

爲何?

既然async函數已被原生支持,還有理解它工做原理的必要嗎?git

呃,除了由於好奇它的原理以外,更重要的是爲了去支持舊的運行平臺。若是你但願使用了新功能的代碼能夠運行在舊的瀏覽器版本和Node.js版本,你可能須要使用諸如Babel這樣的工具去轉換這些新特性。github

所以,深入理解async函數如何被分解成generatorspromises後,在你閱讀或調試轉換後的代碼能派上很大用場。好比,這是一個簡單的async函數:promise

Babel轉換成ES2016代碼以下(不用徹底看懂,下文會解釋):瀏覽器

二者差別很大!固然,若是你理解了async的工做原理,那麼這段轉換以後的代碼對你來講也是小菜一碟。ecmascript

另外一個有趣的事實是,瀏覽器也會將async函數進行實現:瀏覽器像Babel同樣利用generatorspromises轉換async異步

那麼到底發生了些什麼?

有些時候,爲了理解一些東西如何運做,最好的方法就是本身動手作。async

好比咱們有一段使用了async函數的代碼片斷,咱們如何利用generatorspromises去重寫它呢?函數

這是咱們的async函數:

函數體中依次執行三個異步任務,每一個任務依賴前一個任務的完成。最後,函數返回最後一個任務的結果。

如何使用generators重寫

生成器的功能是:能夠退出並再次進入。讓咱們快速回顧一下它的工做方式,如下是一個簡單的generator函數:

這個生成器函數gen擁有一些有趣的特性(從MDN摘取):

  1. 當一個generator函數被調用,函數體內代碼並不當即執行。它返回一個遵循了迭代器協議迭代器對象:它有next方法
  2. 執行gen函數體內代碼的惟一方法就是在返回的迭代器對象上調用next方法。每一次調用next,函數體內代碼就執行到一個yield表達式處,這個表達式的右值賦值給迭代器
  3. next方法也能夠接受參數,使用參數調用將會用參數值替換上一條yield表達式的左值,而後執行並返回當前yield表達式的右值
const a = yiled foo();
// | |
// | |
// 左值 右值
複製代碼

請反覆理解上述步驟或者參考MDN文檔

這些特性如何幫助咱們?

到目前爲止,你可能會疑惑,generator函數如何表達本文意圖?

咱們須要創建一個異步工做流模型:即咱們須要進行下一步時,必須等待特定任務結束。

可是到目前爲止,咱們討論的東西都是同步的。怎麼辦?

譯者注:上文的yield表達式後面全是同步值

關鍵點是生成器函數能夠對promises進行yield

一個generator函數能夠對promise進行yield,而且它的迭代器能夠被控制中止並等待promise最終resolvereject並對他們決議的值進行下一步處理。這種構造一個可yield promises的迭代器的模式能夠知足咱們的需求:

Notice how this generator function resembles our async function!

目前爲止咱們只進行到一半。咱們須要一個執行函數體內容的方法,咱們須要一個能夠控制generator函數迭代器的函數,它可以中止並等待每個yield promise決議的結果。聽上去很複雜,可是實現起來仍是很簡單的 :

A function that executes a generator function. (Only for explanation, do not use it !)

如今咱們能夠像下面同樣去使用runner函數執行咱們的生成器函數init

Use `runner` to execute the body of `init`.

就這麼簡單!runner函數和init函數的組合使用達到了原生async函數的效果。

請切記這個runner函數僅僅是爲了講解本文意圖而作的演示代碼,它不適合實際開發場景,若是你須要一個合適的實現,你能夠在這裏找找。

總結

咱們開始於一個async函數,而後利用generatorspromises去實現相同功能:

深刻實踐

  • 本文伊始,咱們看到BabelES2017async函數轉換以後,如何利用ES2016generatorspromises去實現。你能夠回顧一下以前轉換以後的_asyncToGenerator函數,比較咱們的runner函數就會發現二者很類似。實際上,_asyncToGenerator函數是咱們這裏極其簡單的runner函數萬無一失的版本

  • 若是你還有興趣,你能夠進行下一步研究,即把async函數轉換成沒有generatorsES2015版本代碼。這樣你可能須要去模擬generators自己(參見regenerator project

我但願經過這篇文章撥開async函數的迷霧,他提供了簡單的語法,減小了代碼噪聲。async函數的提議是這樣描述的:

The introduction of Promises and Generators in ECMAScript presents an opportunity to dramatically improve the language-level model for writing asynchronous code in ECMAScript.

感謝Akos, Alisa以及Kristian爲完善這篇文章所提供的反饋。

相關文章
相關標籤/搜索