[Javascript] ES7 Async Await 聖經

在ES5 時期,Javascript 開發者生活在一個地獄,,叫做Callback Hell。每天面對著千層 Callback Function,痛苦地通過加入 console.log 進行 Debug,極度消磨生命。直到ES6的出現,Promise 被指派去拯救開發者脫離 煉獄,神奇的 Promise 把千層 Callback Function 扁平化,轉換成為串聯結構,Promise是黑暗隧道中的一道光。今天,Async/Await 是 Promise 的化身,降臨在ES7中。

文章內容:

備註: 文章主要討論 async/await 在處理非同步的議題.

  1. 必須了解的 asyncFunction
  2. Promise function
  3. Async function
  4. Await Sequentially
  5. Await Parallelly
  6. Await Nest
  7. Await Dynamically
  8. Error Handle
Chronological Picture

1- Constructor of async: asyncFunction

如果你知道 async function 在宣告時發生什麼事,可以跳過這個部份。雖然這是 async 中最重要的觀念,但居我觀察身邊的工程師,其他很少人會知道裏面到底發生什麼事。
去了解 async function 的根本,最好透過 async function的 constructor ,測試的回傳結果是, asyncFunction Object (From Mozilla):

//Node.js
console.log(async function () {});
// Chrome or Firefox
console.log(async function () {}.constructor);
console.log(async function () {}.__proto__);

2- Constructing Promise function

噢!又是 Promises?為什麼一篇聊 async/await 的文章內要穿插著大量的其他內容?因為很多人會忽略,其實async/await 是建立在Promises 的基礎上。
每一個 async 的建立和每一個 await 的等待都是操作著 Promises 的小把戲:

var sleep = function(para) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(para * para)
}, 1000)
})
}
var result = await sleep(2)
// result is 4

3- Constructing Async function

通過 async function 定議的 function/Object 就是一個回傳AsyncFunction 的 Object。
使用 async 處理非同步的 Promise function,回傳的值其實是 Resolve 的值;相似的, async exception 的結果和 Promise Reject 的結果也是一模一樣(Mozilla)。

async function asyncSleep (para){
return await sleep(para)
}
var result = await asyncSleep(2)
//result is 4
asyncSleep(3).then(function(result2){
//result2 is 9
})

4- Await Sequentially

var result1 = await sleep(2);
var result2 = await sleep(result1);
var result3 = await sleep(result2);
//result1 is 4
//result2 is 16
//result3 is 256

5- Await Parallelly

var results = await Promise.all([sleep(1), sleep(2)]);
//results is [1,4]

6- Nest

for(var i =0 ; i<3; i++){
var result = await sleep(i);
for(var j =0 ; j<result; j++){
console.log(' i:'+i+', j:'+j+': ', await sleep(j));
}
}
// i:1, j:0:  0
// i:2, j:0: 0
// i:2, j:1: 1
// i:2, j:2: 4
// i:2, j:3: 9

7- Dynamic Async functions

想通過 Promise 來解決 Dynamic 運算的問題并不容易,往往需要改變䇿略或安裝其他 Module。雖然 Async/Await 的功能強大,但是對處理 Dynamic 的問題也沒有現成的解決方案。

目前的解決方法是,加入 ES6 的新功能 map(),在儲存 Promises function 的同時把 parameters 也存起來,在需要執行時再進行呼叫。

var sleep = function(para1,para2) {
var _para1 = para1, _para2 = para2 || para1 ;
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(_para1 * _para2)
}, 1000)
})
}
var proMap = new Map();
proMap.set([1], sleep);
proMap.set([2, 3], sleep);
proMap.set([3], sleep);
for (var [para, fun] of proMap.entries()) {
var result = await fun.apply(this, para);
console.log(para, result)
}
//[ 1 ] 1
//[ 2, 3 ] 6
//[ 3 ] 9

8- Error handle

錯誤處理的聲音實在安靜,安靜得聽不見 (Nolan Lawson)

上面是一句在 Nolan Lawson 文章內名句,我非常認同。
同媽生下的 Async 和 Promises 在處理 Error Handling 的行為上,完全是執行同一模式,程式都必需必包在 try/catch內錯誤才能被捕獲。

var errorSleep = function(para) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
reject(' ErrorSleep')
}, 1000)
})
}
try {
var result1 = await sleep(1);
var result2 = await errorSleep(4);
var result3 = await sleep(1);
console.log('result1: ', result1)
console.log('result2: ', result2)
console.log('result3: ', result3)
} catch (err) {
console.log('err: ', err)
console.log('result1: ', result1)
console.log('result2: ', result2)
console.log('result3: ', result3)
}
//err:   ErrorSleep
//result1: 1
//result2: undefined
//result3: undefined

Sumary:

好處

async/await 的貢獻在語法上為 Javascripte 進行大優化,原本執行多行 Promises 程式簡化成一行,不僅僅提高程式的可讀性,也是為 functional programming 量身訂造的設計。

壞處

正如第7和8部份提到,Promises 在處理 Dynamic Asynchronize 沒問題沒有進步。只能透過較迂迴的路,使用其他 iterable 方法分別儲存 parameters和 function 。
Async/Await 的另一個問題,錯誤處理的聲音實在太安靜,聽不見。開發者多麼希望,當 Error發生時,JS 能夠大聲大聲的呼叫我!

喜歡這篇文章嗎? 你覺得對其他人有幫助嗎? 請分享給你的朋友和讓我知道你喜歡這類型的文章, 按下面的讚。