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