以前有個段子講一個小偷,潛入某神秘機構,偷出代碼最後一頁,打開一看:
什麼?這隻是段子不是現實?那看看現實版快滴打車的源代碼:

因為Javascript的異步特性,每個開發者都無法避免會碰到一些callback hell,同時在代碼的疊代過程當中因為這樣一些callback hell導緻代碼越來越不可維護。尤其是當回調過程中去參雜一些同步邏輯判斷,那都是疊代過程中的代碼殺手。
産品:我們需要從伺服器取資料,然後再xxx
開發:搞定
産品:我們要插個小功能,取另一分資料,然後再xxx
開發:ok
産品:需要在取第二份資料前加個小判斷,部分使用者不需要取第二份資料。
開發:改起來會有點麻煩。
産品:不就加個條件判斷麼?怎麼會麻煩。
開發:...
産品:再幫我加一個很小的功能。
其實JavaScript 一直在避免回調地獄的問題做出努力,比如<code>async.js</code>,<code>then.js</code>包括ES6下的<code>Promise</code>,<code>co generator</code>等等。這裡不去細講,想進一步了解這些解決方案的差異的話可以看尤雨溪大神在直呼上的回答:
nodejs異步控制「co、async、Q 、『es6原生promise』、then.js、bluebird」有何優缺點?最愛哪個?哪個簡單?
這裡要講的是一種更平滑更接近同步體驗的一種方案<code>Async Functions</code>。
<code>async/await</code>文法最早是在<code>C#5.0</code>中引入,引入後引起了一緻好評,是以使用異步程式設計最多的<code>JavaScript</code>迫不及待的向ES2016(ES7)送出了草案,但因為某些原因,呼聲很高的<code>Async Functions</code>并沒能趕上ES2016的deadline,估計最晚會在ES2017中加入到正式規範,但是并不妨礙我們在<code>Babel</code>的幫助下在ES5的環境下使用它。
先看看在使用<code>Async Functions</code>的情況下,上面産品需求的代碼開發将會怎麼實作:
加入了神奇的<code>async</code>和<code>await</code>關鍵字後,上面的異步回調完全以同步的方式展現,也不用去擔心産品需要再在某個回調中插入流程了而且導緻代碼結構大面積改動了。
<code>babel</code>編譯<code>Async Functions</code>需要<code>transform-async-to-generator</code>插件,參考官方文檔安裝。
寫上測試代碼<code>src/index.js</code>:
babel配置檔案<code>.babelrc</code>如下:
執行指令編譯:
編譯後主要代碼如下:
編譯後的代碼和<code>co</code>很相似,可以了解為基于ES6的<code>Promise</code>和<code>Generator</code>的文法糖。
是以要進一步運作在浏覽器環境下我們還需要使用<code>ES2015 presets</code>和<code>transform-runtime</code>插件。
編譯後關鍵代碼如下:
可以看到編譯後代碼是由狀态機實作,并無任何ES5下不相容代碼。
在使用<code>Promise</code>時,我們有<code>resolve</code>和<code>reject</code>,如下:
在<code>Async Functions</code>中寫法如下:
在<code>async</code>是使用<code>throw</code>相當于<code>Promise</code>中的<code>reject</code>:
在<code>Async</code>函數中, 傳回值永遠是<code>Promise</code>
因為同步非阻塞的表現,是以在循環中使用<code>async</code>将會比以前的代碼更易讀明了。
同樣在匿名函數中可以一樣去使用<code>async</code>關鍵字,如下:
代碼一:
運作完需要6秒。
代碼二:
代碼二運作完卻隻要3秒,因為sleep是在同一時間運作的。
結束語:<code>async/await</code> 無疑是現階段最好的異步回調同步化的解決方案,不過因為暫時沒有納入ES2016規範,而且主流浏覽器的支援的不足,是以我們隻能通過使用<code>babel</code>嘗鮮。但是我們也可以借此看到未來<code>JavaScript</code>在回調問題上的主流解決方案。