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