摘要:所以是在一秒后顯示的。這個行為不會耗費資源,因為引擎可以同時處理其他任務(wù)執(zhí)行其他腳本,處理事件等。每個回調(diào)首先被放入微任務(wù)隊列然后在當(dāng)前代碼執(zhí)行完成后被執(zhí)行。,函數(shù)是異步的,但是會立即運行。否則,就返回結(jié)果,并賦值。
「async/await」是 promises 的另一種更便捷更流行的寫法,同時它也更易于理解和使用。
Async functions讓我們以 async 這個關(guān)鍵字開始。它可以被放置在任何函數(shù)前面,像下面這樣:
async function f() { return 1; }
在函數(shù)前面的「async」這個單詞表達(dá)了一個簡單的事情:即這個函數(shù)總是返回一個 promise。即使這個函數(shù)在語法上返回了一個非 promise 的值,加了「async」這個關(guān)鍵字就會指示 JavaScript 引擎自動將返回值包裝成一個解析后的 promise。
例如,以下的代碼就返回了一個以 1 為結(jié)果的解析后的 promise, 讓我們試一下:
async function f() { return 1; } f().then(alert); // 1
... 我們也可以顯式返回一個 promise,結(jié)果是一樣的:
async function f() { return Promise.resolve(1); } f().then(alert); // 1
所以說,async 確保了函數(shù)的返回值是一個 promise,也會包裝非 promise 的值。很簡單是吧?但是還沒完。還有一個關(guān)鍵字叫 await,它只在 async 函數(shù)中有效,也非???。
Await語法如下:
// 只在 async 函數(shù)中有效 let value = await promise;
關(guān)鍵字 await 讓 JavaScript 引擎等待直到 promise 完成并返回結(jié)果。
這里的例子就是一個 1 秒后解析的 promise:
async function f() { let promise = new Promise((resolve, reject) => { setTimeout(() => resolve("done!"), 1000) }); let result = await promise; // 等待直到 promise 解析 (*) alert(result); // "done!" } f();
這個函數(shù)在執(zhí)行的時候,「暫?!乖诹?(*) 那一行,并且當(dāng) promise 完成后,拿到 result 作為結(jié)果繼續(xù)往下執(zhí)行。所以「done!」是在一秒后顯示的。
劃重點:await 字面的意思就是讓 JavaScript 引擎等待直到 promise 狀態(tài)完成,然后以完成的結(jié)果繼續(xù)執(zhí)行。這個行為不會耗費 CPU 資源,因為引擎可以同時處理其他任務(wù):執(zhí)行其他腳本,處理事件等。
相比 promise.then 來獲取 promise 結(jié)果,這只是一個更優(yōu)雅的語法,同時也更易書寫。
不能在普通函數(shù)中使用 await
如果我們嘗試在非 async 函數(shù)中使用 await 的話,就會報語法錯誤:
function f() { let promise = Promise.resolve(1); let result = await promise; // 語法錯誤 }
如果函數(shù)前面沒有 async 關(guān)鍵字,我們就會得到一個語法錯誤。就像前面說的,await 只在 async 函數(shù) 中有效。
讓我們拿 Promises 鏈那一章的 showAvatar() 例子改寫成 async/await 的形式:
用 await 替換掉 .then 的調(diào)用
在函數(shù)前面加上 async 關(guān)鍵字
async function showAvatar() { // 讀取 JSON let response = await fetch("/article/promise-chaining/user.json"); let user = await response.json(); // 讀取 github 用戶信息 let githubResponse = await fetch(`https://api.github.com/users/${user.name}`); let githubUser = await githubResponse.json(); // 顯示頭像 let img = document.createElement("img"); img.src = githubUser.avatar_url; img.className = "promise-avatar-example"; document.body.append(img); // 等待 3 秒 await new Promise((resolve, reject) => setTimeout(resolve, 3000)); img.remove(); return githubUser; } showAvatar();
簡潔明了,是吧?比之前可強多了。
await 不能在頂層代碼運行
剛開始使用 await 的人常常會忘記 await 不能用在頂層代碼中。如,下面這樣就不行:
// 用在頂層代碼中會報語法錯誤 let response = await fetch("/article/promise-chaining/user.json"); let user = await response.json();
我們可以將其包裹在一個匿名 async 函數(shù)中,如:
(async () => { let response = await fetch("/article/promise-chaining/user.json"); let user = await response.json(); ... })();
await 可以接收「thenables」
像 promise.then 那樣,await 被允許接收 thenable 對象(具有 then 方法的對象)。有些對象雖然不是 promise,但是卻兼容 promise,如果這些對象支持 .then,那么就可以對它們使用 await。
下面是一個 Thenable 類,await 接收了該類的實例:
class Thenable { constructor(num) { this.num = num; } then(resolve, reject) { alert(resolve); // 1 秒后解析為 this.num*2 setTimeout(() => resolve(this.num * 2), 1000); // (*) } }; async function f() { // 等待 1 秒, result 變?yōu)?2 let result = await new Thenable(1); alert(result); } f();
如果 await 接收了一個非 promise 的但是提供了 .then 方法的對象,它就會調(diào)用這個 then 方法,并將原生函數(shù) resolve,reject 作為參數(shù)傳入。然后 await 等到這兩個方法中的某個被調(diào)用(在例子中發(fā)生在(*)的那一行),再處理得到的結(jié)果。
Async methods
如果想定義一個 async 的類方法,在方法前面添加 async 就可以了:
class Waiter { async wait() { return await Promise.resolve(1); } } new Waiter() .wait() .then(alert); // 1異常處理
如果一個 promise 正常解析,await promise 返回的就是其結(jié)果。但是如果 promise 被拒絕,就會拋出一個錯誤,就像在那一行有個 throw 語句那樣。
這里的代碼:
async function f() { await Promise.reject(new Error("Whoops!")); }
...和下面是一樣的:
async function f() { throw new Error("Whoops!"); }
在真實的環(huán)境下,promise 被拒絕前通常會等待一段時間。所以 await 會等待,然后拋出一個錯誤。
我們可以用 try...catch 來捕獲上面的錯誤,就像對一般的 throw 語句那樣:
async function f() { try { let response = await fetch("http://no-such-url"); } catch(err) { alert(err); // TypeError: failed to fetch } } f();
如果有錯誤發(fā)生,代碼就會跳到 catch 塊中。當(dāng)然也可以用 try 包裹多行 await 代碼:
async function f() { try { let response = await fetch("/no-user-here"); let user = await response.json(); } catch(err) { // 捕獲到 fetch 和 response.json 中的錯誤 alert(err); } } f();
如果我們不使用 try...catch,由f() 產(chǎn)生的 promise 就會被拒絕。我們可以在函數(shù)調(diào)用后添加 .catch 來處理錯誤:
async function f() { let response = await fetch("http://no-such-url"); } // f() 變?yōu)橐粋€被拒絕的 promise f().catch(alert); // TypeError: failed to fetch // (*)
如果我們忘了添加 .catch,我們就會得到一個未處理的 promise 錯誤(顯示在控制臺)。我們可以通過在錯誤處理與 Promise 章節(jié)講的全局事件處理器來捕獲這些。
async/await 和 promise.then/catch
當(dāng)我們使用 async/await 時,幾乎就不會用到 .then 了,因為為我們await 處理了異步等待。并且我們可以用 try...catch 來替代 .catch。這通常更加方便(當(dāng)然不是絕對的)。
但是當(dāng)我們在頂層代碼,外面并沒有任何 async 函數(shù),我們在語法上就不能使用 await 了,所以這時候就可以用 .then/catch 來處理結(jié)果和異常。
就像上面代碼的 (*) 那行一樣。
async/await 可以和 Promise.all 一起使用
當(dāng)我們需要同時等待多個 promise 時,我們可以用 Promise.all 來包裹他們,然后使用 await:
// 等待多個 promise 結(jié)果 let results = await Promise.all([ fetch(url1), fetch(url2), ... ]);
如果發(fā)生錯誤,也會正常傳遞:先從失敗的 promise 傳到 Promise.all,然后變成我們能用 try...catch 處理的異常。
Microtask queue我們在微任務(wù)和事件循環(huán)章節(jié)講過,promise 回調(diào)是異步執(zhí)行的。每個 .then/catch/finally 回調(diào)首先被放入「微任務(wù)隊列」然后在當(dāng)前代碼執(zhí)行完成后被執(zhí)行。
Async/await 是基于 promise 的,所以它內(nèi)部使用相同的微任務(wù)隊列,并且相對宏任務(wù)來說具有更高的優(yōu)先級。
例如,看代碼:
setTimeout(handler, 0),應(yīng)該以零延遲運行 handler 函數(shù)。
let x = await f(),函數(shù) f() 是異步的,但是會立即運行。
那么如果 await 在 setTimeout 下面,哪一個先執(zhí)行呢?
async function f() { return 1; } (async () => { setTimeout(() => alert("timeout"), 0); await f(); alert("await"); })();
這里很確定:await 總是先完成,因為(作為微任務(wù))它相比 setTimeout 具有更高的優(yōu)先級。
總結(jié)函數(shù)前面的關(guān)鍵字 async 有兩個作用:
讓這個函數(shù)返回一個 promise
允許在函數(shù)內(nèi)部使用 await
這個 await 關(guān)鍵字又讓 JavaScript 引擎等待直到 promise 完成,然后:
如果有錯誤,就會拋出異常,就像那里有一個 throw error 語句一樣。
否則,就返回結(jié)果,并賦值。
這兩個關(guān)鍵字一起用就提供了一個很棒的方式來控制異步代碼,并且易于讀寫。
有了 async/await 我們就幾乎不需要使用 promise.then/catch,但是不要忘了它們是基于 promise 的,所以在有些時候(如在最外層代碼)我們就可以用 promise 的形式。再有就是 Promise.all 可以幫助我們同時處理多個異步任務(wù)。
原文鏈接文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/104748.html
摘要:原文地址原文作者翻譯作者是在版本中引入的,它對于中的異步編程而言是一個巨大的提升??赡軙a(chǎn)生誤導(dǎo)一些文章把和進(jìn)行了比較,同時說它是異步編程演變過程中的下一代解決方案,對此我不敢茍同。結(jié)論在中引入的關(guān)鍵字無疑是對異步編程的一大加強。 原文地址: https://hackernoon.com/javasc...原文作者: Charlee Li 翻譯作者: Xixi20160512 asy...
摘要:前端日報精選開發(fā)常見問題集錦前端碼農(nóng)的自我修養(yǎng)虛擬內(nèi)部是如何工作的譯知乎專欄并不慢,只是你使用姿勢不對一份優(yōu)化指南掘金老司機帶你秒懂內(nèi)存管理第一部中文免費公開課前端面試的大關(guān)鍵點,你到了嗎知乎專欄高效開發(fā)與設(shè)計姐的圖片二三 2017-07-19 前端日報 精選 VueJS 開發(fā)常見問題集錦 - 前端碼農(nóng)的自我修養(yǎng) - SegmentFault虛擬 DOM 內(nèi)部是如何工作的?[譯]Hig...
摘要:問題的關(guān)鍵在于其執(zhí)行過程中的微任務(wù)數(shù)量,下文中我們需要用上述代碼中的方式對微任務(wù)的執(zhí)行順序進(jìn)行標(biāo)記,以輔助我們理解這其中的執(zhí)行過程。 原文發(fā)布在掘金社區(qū):https://juejin.im/post/5c3cc981f265da616a47e028 起源 2019年了,相信大家對 Promise 和 async/await 都不再陌生了。 前幾日,我在社區(qū)讀到了一篇關(guān)于 async/...
閱讀 814·2023-04-26 01:30
閱讀 3366·2021-11-24 10:32
閱讀 2269·2021-11-22 14:56
閱讀 2094·2021-11-18 10:07
閱讀 612·2019-08-29 17:14
閱讀 694·2019-08-26 12:21
閱讀 3163·2019-08-26 10:55
閱讀 3016·2019-08-23 18:09