[譯] JavaScript async 和 await

原文連接:davidwalsh.name/async-awaitjavascript

JavaScript Promise 是替代傳統回調函數的一個方案,是回調函數的一個改進版。但使用 Promise 會讓代碼中大量出現 then 方法,一長串的那種。ES2017 引入了一種新的處理異步任務的方式----async 函數,它比使用 Promise API 更加簡潔。java

引入的 async 函數,使用了兩個新的關鍵字:async 和 await。web

快速預覽

  • async 關鍵字在函數聲明前使用。
  • await 用於處理 Promise 對象。
  • await 只能用在 async 函數中。
  • async 函數老是返回一個 Promise 對象,不論函數是否 return Promise 對象。
  • async/await 和 Primose 對象在本質上是同樣的。

使用 async 和 await 的好處

  • 代碼更加簡潔、精確。
  • 由於少回調,Debug 起來更容易。
  • 從 Promise then/catch 書寫形式過渡過來很是天然。
  • 代碼更加「自上而下」,少嵌套。

async 和 await 的基本使用

一例勝千言,先看一個簡單的使用 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 例子改寫成 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

匿名 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 後結束後,輪到第二個請求的時候,也已經完成並當即就能返回結果。這種狀況適應於無依賴的請求之間。

await Promise.all

我最喜歡的 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,在維護性上略輸一籌。

(完)

相關文章
相關標籤/搜索