這篇文章我將介紹ES2017
的async
函式為什麼是ES2016
的Generators
和Promises
特性功能的語法糖。
閱讀須知
- 本文不對三者概念進行介紹和講解
- 本文唯一的目的就是介紹如何利用
Generators
和Promises
去實現async
- 本文對
async
和其他相似實現不進行優劣評價 - 本文程式碼都是經過巧妙設計以便於理解,他們不適用於實際開發
為什麼?
既然async
函式已被原生支援,還有理解它工作原理的必要嗎?
呃,除了因為好奇它的原理之外,更重要的是為了去支援舊的執行平臺。如果你希望使用了新功能的程式碼可以執行在舊的瀏覽器版本和Node.js
版本,你可能需要使用諸如Babel
這樣的工具去轉換這些新特性。
因此,深刻理解async
函式如何被分解成generators
和promises
後,在你閱讀或除錯轉換後的程式碼能派上很大用場。比如,這是一個簡單的async
函式:
被Babel
轉換成ES2016
程式碼如下(不用完全看懂,下文會解釋):
兩者差異很大!當然,如果你理解了async
的工作原理,那麼這段轉換之後的程式碼對你來說也是小菜一碟。
另一個有趣的事實是,瀏覽器也會將async
函式進行實現:瀏覽器像Babel
一樣利用generators
和promises
去轉換async
。
那麼到底發生了些什麼?
有些時候,為了理解一些東西如何運作,最好的方法就是自己動手做。
比如我們有一段使用了
async
函式的程式碼片段,我們如何利用generators
和promises
去重寫它呢?
這是我們的async
函式:
函式體中依次執行三個非同步任務,每個任務依賴前一個任務的完成。最後,函式返回最後一個任務的結果。
如何使用generators
重寫
生成器的功能是:可以退出並再次進入。讓我們快速回顧一下它的工作方式,以下是一個簡單的generator
函式:
這個生成器函式gen
擁有一些有趣的特性(從MDN摘取):
- 當一個
generator
函式被呼叫,函式體內程式碼並不立即執行。它返回一個遵循了迭代器協議的迭代器
物件:它有next
方法 - 執行
gen
函式體內程式碼的唯一方法就是在返回的迭代器
物件上呼叫next
方法。每一次呼叫next
,函式體內程式碼就執行到一個yield
表示式處,這個表示式的右值
賦值給迭代器
。 next
方法也可以接受引數,使用引數呼叫將會用引數值替換上一條yield
表示式的左值
,然後執行並返回當前yield
表示式的右值
const a = yiled foo();
// | |
// | |
// 左值 右值
複製程式碼
請反覆理解上述步驟或者參考MDN文件。
這些特性如何幫助我們?
到目前為止,你可能會疑惑,generator
函式如何表達本文意圖?
我們需要建立一個非同步工作流模型:即我們需要進行下一步時,必須等待特定任務結束。
但是到目前為止,我們討論的東西都是同步的。怎麼辦?
譯者注:上文的
yield
表示式後面全是同步值
關鍵點是生成器函式可以對
promises
進行yield
一個generator
函式可以對promise
進行yield
,並且它的迭代器
可以被控制停止並等待promise
最終resolve
或reject
並對他們決議的值進行下一步處理。這種構造一個可yield promises
的迭代器的模式可以滿足我們的需求:
目前為止我們只進行到一半。我們需要一個執行函式體內容的方法,我們需要一個可以控制generator
函式迭代器
的函式,它能夠停止並等待每一個yield promise
決議的結果。聽上去很複雜,但是實現起來還是很簡單的 :
現在我們可以像下面一樣去使用runner
函式執行我們的生成器函式init
:
就這麼簡單!runner
函式和init
函式的組合使用達到了原生async
函式的效果。
請切記這個runner
函式僅僅是為了講解本文意圖而做的演示程式碼,它不適合實際開發場景,如果你需要一個合適的實現,你可以在這裡找找。
總結
我們開始於一個async
函式,然後利用generators
和promises
去實現相同功能:
深入實踐
-
本文伊始,我們看到
Babel
將ES2017
的async
函式轉換之後,如何利用ES2016
的generators
和promises
去實現。你可以回顧一下之前轉換之後的_asyncToGenerator
函式,比較我們的runner
函式就會發現兩者很相似。實際上,_asyncToGenerator
函式是我們這裡極其簡單的runner
函式萬無一失的版本 -
如果你還有興趣,你可以進行下一步研究,即把
async
函式轉換成沒有generators
的ES2015
版本程式碼。這樣你可能需要去模擬generators
本身(參見regenerator project)
我希望通過這篇文章撥開async
函式的迷霧,他提供了簡單的語法,減少了程式碼噪聲。async
函式的提議是這樣描述的: