原文連接:davidwalsh.name/async-awaitjavascript
JavaScript Promise 是替代傳統回調函數的一個方案,是回調函數的一個改進版。但使用 Promise 會讓代碼中大量出現 then 方法,一長串的那種。ES2017 引入了一種新的處理異步任務的方式----async 函數,它比使用 Promise API 更加簡潔。java
引入的 async 函數,使用了兩個新的關鍵字:async 和 await。web
一例勝千言,先看一個簡單的使用 async 和 await 的例子。編程
// 將函數聲明爲一個 async 函數,這樣就能在內部使用 await 了
async function fetchContent() {
// 使用 await,而非 fetch.then
let content = await fetch('/');
let text = await content.text();
// async 函數最終返回一個 resolved 狀態的 Promise 對象,
// Promise 對象的 then 回調方法接收的參數就是這裏的 text
return text;
}
// 調用 async 函數
let promise = fetchContent.then(...);
複製代碼
async 函數以 async 關鍵字標記,await 只能用在 async 函數中。await 後面緊跟的是生成 Promise 對象的(promise-yielding)操做,對應這裏的 fetch API。只有等到第一個 await 後面的操做完成後,纔會繼續執行後面的代碼。最終,async 函數返回一個 resolved 狀態的 Promise 對象,而這個 Promise 對象的 then 回調方法中,接收的參數就是 text。json
讓咱們看一下,怎麼將一個 Promise 例子改寫成 async 函數的形式。promise
// 以前:回調城!
fetch('/users.json')
.then(response => response.json())
.then(json => {
console.log(json);
})
.catch(err = {
console.log(err);
});
// 以後:再也不有任何回調!
async function getJson() {
try {
let response = await fetch('/users.json');
let json = await response.json();
console.log(json);
} catch (err) {
console.log(err);
}
}
複製代碼
是否是代碼變簡潔、好看了呢。異步
下面介紹了幾種使用 async 函數的場景和方式。async
匿名 async 函數函數
let main = (async function () {
return await fetch('/');
})();
複製代碼
async 函數聲明fetch
async function main() {
return await fetch('/');
}
複製代碼
async 函數表達式
let main = async function () {
return await fetch('/');
};
// 也能夠使用箭頭函數哦!
let main = async () => {
return fetch('/');
};
複製代碼
當作參數的 async 函數
document.body.addEventListener('click', async function () {
return await fetch('/');
});
複製代碼
做爲對象和類的方法
// 做爲對象方法
let obj = {
async method() {
return await fetch('/');
}
};
// 做爲類方法
class MyClass {
async method() {
return await fetch('/');
}
}
複製代碼
你看到了,async 函數除了自身提供的超炫功能外,TM 跟普通函數使用起來是同樣同樣的!
傳統 rejected 狀態的 Promise 對象使用 catch 方法捕獲錯誤。而 await 至關因而已經處理了 resolved 狀態下 Promise 對象返回的數據,因此在處理錯誤時,await 藉助了 try/catch:
try {
let x = await myAsyncFunction();
} catch (err) {
// Error!
}
複製代碼
雖然,try/catch 處理錯誤的方式看起來很老套,可是相對於引入 await 給咱們帶來的便捷,這根本算不上什麼(要否則還要怎樣)。
Google 的 Jake Archibald 在 async functions 文檔 提出了一個很好的建議----不要同時平行地使用多個 await 語句,這會致使等待時間層層累加,若是可能的話,應該當即發出異步任務,以後再使用 await 等待任務完成(會節省時間的呦)。
// 一共要花掉 1000ms!
async function series() {
await wait(500);
await wait(500);
return "done!";
}
// 僅花掉 500ms!
async function parallel() {
const wait1 = wait(500);
const wait2 = wait(500);
await wait1;
await wait2;
return "done!";
}
複製代碼
第二種狀況讓兩個異步請求同時發出,第一個請求在 500ms 後結束後,輪到第二個請求的時候,也已經完成並當即就能返回結果。這種狀況適應於無依賴的請求之間。
我最喜歡的 Promise API 的功能之一就是 Promise.all
,它會等待全部的 Promise 對象完成以後再處理數據。咱們也能夠在 Promise.all
上使用 await:
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
複製代碼
記住,async/await 和 Primose 對象在本質上是同樣的,這是咱們可以使用 await Promise.all 等待多個 resolved 狀態 Promise 對象返回數據的緣由。
使用 Promise 接口編程仍然很優秀,可是相比於 async 和 await,在維護性上略輸一籌。
(完)