非同步在前端的做法不斷的在進行優化調整,先前介紹過 Promise 可以解決非同步過度巢狀的問題,而本篇要介紹的 async function
(非同步函式) 及 await
則可以將非同步的程式碼寫成類似同步的形式。
Promise 本篇不會詳細介紹,如果不熟悉可以先查看 本篇 文章,Promise 與非同步函式兩者是密不可分的,雖然 async function
易讀性優於 Promise,但請先確保對於 Promise 有一定理解再來使用非同步函式。
本篇先建立一個基於 Promise 的函式,以下的範例都會不斷呼叫此函式來進行撰寫。
/**
* 範例 Promise 函式
*
* @param {數值:作為判斷非同步成功與否的條件} num
* @param {數值:非同步所執行的時間長度} [time=500]
* @returns {如果 num 為真則套用 resolve;失敗則套用 reject}
*/
function promiseFn(num, time = 500) {
return new Promise((resolve, reject) => {
setTimeout(() => {
num ? resolve(`${num}, 成功`) : reject('失敗');
}, time);
});
}
promise
函式呼叫時可以使用 then
來接收 resolve
的結果,當要串接兩個 promise
函式時可以使用 return
來做 "鏈接"。
promiseFn(1)
.then(res => {
console.log(res); // 1, 成功
return promiseFn(2); // 鏈接第二次的 Promise 方法
})
.then(res => {
console.log(res); // 2, 成功
});
基於 Promise 的方法相當單純,就是不斷的呼叫以及使用 then
來進行鏈接,當程式碼中只有呼叫 promise 函式時不會有太大問題,但如果有其它的方法需要進行運行時,就會顯得 promise 格格不入。
Promise
相當不錯,可以解決過巢及串接 callback function
語法不一致等問題,但它依然在 JS 的同步語言中插入了一段非同步的片段。
async function
可以用來定義一個非同步函式,讓這個函式本體是屬於非同步,但其內部以“同步的方式運行非同步”程式碼。
await
則是可以暫停非同步函式的運行(中止 Promise
的運行),直到非同步進入 resolve
或 reject
,當接收完回傳值後繼續非同步函式的運行。
Promise 的回傳狀態,需要進入
resolve
或reject
後,非同步函式才會繼續運行
上述以 promise
、then
寫法的程式碼,以非同步的程式碼改寫方式如下:
async function
)await
暫停 promiseFn
,直到回傳後再繼續向下async function getData() {
const data1 = await promiseFn(1); // 因為 await,promise 函式被中止直到回傳
const data2 = await promiseFn(2);
console.log(data1, data2); // 1, 成功 2, 成功
}
getData();
以上這段程式碼的結果與使用 then
是一致的,但就結構上更加平整,在 getData
這個函式中都是以 "同步" 的方式運行,不會產生同步、非同步混合的狀況。
無論是不是 Promise,大家都是同步程式碼逐行執行。
Async / Await 目的是讓程式碼的結構變得更加簡潔、易懂,所以運用上也如上述一樣單純(如果沒有 “錯誤”,它確實很單純),本段則額外補充說明這兩者新加入的語法是如何在 JS 中運作的。
async function
非同步函式async function
的用法相當特別,用此語法所宣告的函式,可在其內以“同步的方式運行非同步”程式碼;但就名稱上 async
是稱為非同步,那麼它的 非同步
又存在哪呢?
async function asyncFn() {
return 'a';
}
console.log(asyncFn());
非同步
稱的就是 async function
所定義的函式本體,當使用 console.log
查看 async function
那麼將可以得到與 Promise 結構相似的函式,該函式是以非同步的方式運行,無法直接使用 console.log
取得其值。
在 Promise 中,如果要取得 resolve
的結果會使用 then
,而 async function
也是相同使用 then()
。
asyncFn().then(r => {
console.log(r)
});
雖說如此,實戰中不太會這麼做,回到目的性來說:「
async function
是讓函式內的語法同步執行」。也因為async function
與一般函式定義的不同,所以請避免將所有的function
前方都補上async
,這會產生運行及概念上不同的函式。
await
是屬於一元運算子,它會直接回傳後方表達式的值;但如果是 Promise 時則會 “等待” resovle 的結果並回傳。
雖然是運算子,但是在原始碼中直接運行 await
則會出現錯誤,它只能在 async function
中運行,所以 async/await
基本上是一體的,不會單獨出現。
await 1;
// Uncaught SyntaxError: await is only valid in async function
比較神奇的是
await expression
卻可以直接在 Chrome console 中運行。
await
可以直接回傳後方的表達式,或者將非同步函式中的 Promise 暫停,如以下範例的 await promiseFn(2)
會 “等待” resolve
結果回傳後,在賦予至 data2
才會回傳。
async function getData() {
const data1 = await 1;
const data2 = await promiseFn(2);
console.log(data1, data2); // 1 "2, 成功"
}
getData();
本篇是 async/await
兄弟的基礎介紹,接下來的章節會持續介紹關於這兩者的錯誤流程、延伸運用等技巧。