「async/await」是一種與「promise」協做的特殊的語法。它使得異步工做更加容易理解和使用。javascript
咱們從async關鍵詞開始,它能夠被放置在任何函數的開頭位置,好比:java
async function f() { return 1; }
這裏的async表示:該函數將始終返回一個promise。即便您的代碼沒有顯式返回一個promise,在JavaScript運行時也會自動包裝一個promise,用於返回指定的值。git
在這個例子中,這段代碼將會返回一個result爲1的promise:github
async function f() { return 1; } f().then(alert); // 1
固然,咱們也能夠顯式的返回一個promise:json
async function f() { return Promise.resolve(1); } f().then(alert); // 1
async確保了函數會返回一個promise。挺簡單的對吧?接下來,是另外一個關鍵詞await,僅僅能在async標記的函數中生效。api
語法說明:promise
//該段代碼僅僅能在 async 標記的函數中生效 let value = await promise;
關鍵詞await確保JavaScript運行時將會等待promise執行完畢並返回結果。
下面是一段使用promise並在一秒後返回結果的例子:app
async function f() { let promise = new Promise((resolve, reject) => { setTimeout(() => resolve("done!"), 1000) }); let result = await promise; // 等待至promise得到結果 (*) alert(result); // "done!" } f();
該函數在運行至await時,執行了「pauses」操做,直至promise執行完畢後從新執行接下來的代碼。因此該段代碼將在一秒後顯示「done!」。
讓咱們強調一遍:await將順序使得JavaScript的運行時等待promise執行完畢,然後繼續運行餘下代碼。等待時的操做並不會消耗任何CPU資源,由於此時的運行時能夠同時執行其餘操做:執行其餘的代碼,處理事件邏輯等。
這僅僅是一項相對promise.than更爲優雅的語法來獲取promise的運行結果,更容易閱讀和編寫代碼而已。框架
不能將await用於任何標準函數異步
若是您嘗試將await運行在任何未標記爲async的函數中,都會產生一個語法錯誤
function f() { let promise = Promise.resolve(1); let result = await promise; // Syntax error }
咱們若是沒有使用async標記函數,那麼咱們就會獲得這個語法錯誤。換句話說,await僅能夠運行在async function中。
讓咱們修改 Promises chaining 中的例子,使用async/await來重寫這個例子。
async function showAvatar() { // read our JSON let response = await fetch('/article/promise-chaining/user.json'); let user = await response.json(); // read github user let githubResponse = await fetch(`https://api.github.com/users/${user.name}`); let githubUser = await githubResponse.json(); // show the avatar let img = document.createElement('img'); img.src = githubUser.avatar_url; img.className = "promise-avatar-example"; document.body.append(img); // wait 3 seconds await new Promise((resolve, reject) => setTimeout(resolve, 3000)); img.remove(); return githubUser; } showAvatar();
至關容易閱讀和理解對吧。
人們在開始使用await時,老是容易忘記必須在async function內部使用。好比如下代碼將會報錯:
// syntax error in top-level code let response = await fetch('/article/promise-chaining/user.json'); let user = await response.json();
因此咱們須要聲明一個async function來包裹該段代碼。
相似promise.then,await准許使用then方法。須要申明的是,這裏指的是一個非promise對象,但它持有.then方法,那麼就能夠配合await使用。
好比如下例子,await接受new Thenable(1):
class Thenable { constructor(num) { this.num = num; } then(resolve, reject) { alert(resolve); // function() { native code } // resolve with this.num*2 after 1000ms setTimeout(() => resolve(this.num * 2), 1000); // (*) } }; async function f() { // waits for 1 second, then result becomes 2 let result = await new Thenable(1); alert(result); } f();
若是await與一個含有.then方法的非promise對象組合,同時其支持resolve、reject兩個方法做爲參數。那麼await將會等待其中之一的函數被調用(註釋(*)所在行)並在以後繼續運行剩餘代碼。
類函數亦能夠被定義爲異步函數,僅需將async置於函數聲明前。
相似於:
class Waiter { async wait() { return await Promise.resolve(1); } } new Waiter() .wait() .then(alert); // 1
這和以前的其餘代碼端是同樣的做用:應用await返回一個promise對象。
若是promise順利完成,那麼await promise將返回一個值。但假如觸發了錯誤,那麼將在此行代碼中throw一個錯誤。
如下代碼:
async function f() { await Promise.reject(new Error("Whoops!")); }
等同於:
async function f() { throw new Error("Whoops!"); }
在真實的運行環境中,可能須要消耗一些時間來觸發錯誤。因此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代碼塊。咱們可使用以下代碼:
async function f() { try { let response = await fetch('/no-user-here'); let user = await response.json(); } catch(err) { // catches errors both in fetch and response.json alert(err); } } f();
但若是咱們沒有使用try…catch,那麼將會在異步調用f()時觸發rejected。咱們也能夠添加.catch來處理錯誤:
async function f() { let response = await fetch('http://no-such-url'); } // f() becomes a rejected promise f().catch(alert); // TypeError: failed to fetch // (*)
若是咱們忘記添加.catch,咱們將得到一個未被捕獲的錯誤。咱們也可使用一個全局事件捕獲方法來處理,參見Promise chaining。
當咱們使用async/await時,咱們僅僅須要.then,由於await會接受正確結果。咱們可使用try…catch來取代.catch。同時這也將更爲便利。
但處於頂層代碼邏輯時,咱們的邏輯代碼處在async function之外,咱們並不能直接使用await,因此添加.then/catch代碼塊來獲取最終結果是更爲廣泛的作法。
當咱們須要等待多個promises時,咱們可使用Promise.all以後使用await:
// wait for the array of results let results = await Promise.all([ fetch(url1), fetch(url2), ... ]);
假如觸發了一個錯誤,它也會和其餘promise同樣工做:從運行失敗的Promise.all節點中斷,以後咱們能夠經過try…catch來捕獲錯誤。
async定之後的函數有兩層做用:
await關鍵詞確保js運行時將會等待promise處理完畢,而且:
咱們一塊兒提供了一個偉大的框架來更爲簡便的完成異步操做。
有了async/await的幫組,咱們能夠大幅減小使用promise.then/catch,但咱們依然不該該忘記這些技術是基於promises,極可能咱們會不得不繼續使用promise的方法。同時,Promise.all至關適合等待多個任務順序執行的操做。