1. 程式人生 > 其它 >JavaScript Async/Await

JavaScript Async/Await

在很長一段時間裡,JavaScript 開發人員不得不依賴回撥來處理非同步程式碼。因此,我們中的許多人都經歷過回撥地獄,當面對這樣的函式時,我們會感到無賴

不過好在JavaScript提供了,.then()的一種回覆方式,目前也有很多人正在用他

現在,隨著 Async/Await 的最新增加,編寫 JavaScript 程式碼將會變得更加優雅!

什麼是 Async/Await?

Async/Await 是一個期待已久的 JavaScript 特性,它使得使用非同步函式變得更加愉快和更容易理解。它建立在 Promises 之上,並與所有現有的基於 promise 的 api 相容

這個名字來自 async 和 await ——這兩個關鍵詞可以幫助我們清理非同步程式碼:

Async-宣告一個非同步函式(Async function someName (){ ... })

  1. 自動地將一個普通的函式轉換成一個承諾
  2. 當呼叫非同步函式時,當非同步完成時,然後返回的是非同步內容
  3. 在非同步函式中允許使用await

Await-暫停 async 函式的執行

  1. 當被放置在一個Promise前時, 強制程式碼的其餘部分等待,直到承諾完成並返回結果
  2. 只對 Promises 起作用,對 callback 不起作用
  3. 只能在裡面使用async函式中使用

通過一個簡單的例子來分析

假設我們想從伺服器獲取一些 JSON 檔案。我們將編寫一個使用 axios 庫的函式,並向 https://tutorialzine.com/misc...

傳送一個 HTTP GET 請求。我們必須等待伺服器響應,所以這個 HTTP 請求自然是非同步的

下面我們可以看到同一個函式實現了兩個。首先是使用 Promises,然後是第二個使用 Async/Await

// Promise
function getJSON(){
    // 為了使函式阻塞,我們手動建立一個承諾.
    return new Promise( function(resolve) {
        axios.get('https://tutorialzine.com/misc/files/example.json')
            .then( function(json) {
                // 在.then中獲得返回的json資料
                // 我們使用resolve返回結果
                resolve(json);
            });
    });

}

// Async/Await
// async關鍵字將自動建立一個新的Promise並返回它.
async function getJSONAsync(){

    // await關鍵字使我們不必編寫then().
    let json = await axios.get('https://tutorialzine.com/misc/files/example.json');

    // GET請求的結果在json變數中可用.
    // 獲取資料就像在一個常規的同步函式中一樣
    return json;
}

很明顯,Async/Await 版本的程式碼更短,更容易閱讀。除了使用的語法之外,這兩個函式完全相同——它們都返回 Promises 並使用 axios 的 JSON 響應解析。我們可以這樣呼叫我們的 async 函式:

async返回本身就是一個Promise

getJSONAsync().then( function(result) {
    // Do something with result.
});

Async/Await 會使承諾過時嗎?

不,一點也不。
在使用 Async/Await 時,我們仍然在引擎蓋下使用 Promises。
從長遠來看,充分理解Promise實際上會對你有所幫助,因此強烈推薦你這麼做

甚至在一些情況下 Async/Await 也不能解決問題,我們不得不回到 Promises 尋求幫助。其中一種情況是,我們需要進行多個獨立的非同步呼叫,並等待它們全部完成

如果我們嘗試使用 async 和 await 來實現這個功能,將會發生以下情況:

async function getABC() {
  let A = await getValueA(); // getValueA 需要2秒
  let B = await getValueB(); // getValueB 需要4秒
  let C = await getValueC(); // getValueC 需要3秒
  return A*B*C;
}

每個await等待都將等待await前一個返回結果。因為我們一次只調用一個函式,整個函式從開始到結束(2 + 4 + 3)需要9秒

這不是最佳解決方案,因為三個變數 a、 b 和 c 並不相互依賴。換句話說,在得到 b 之前,我們不需要知道 a 的值。我們可以在同一時間得到它們,減少幾秒鐘的等待時間

在同一時間傳送所有請求。這將確保我們在繼續之前仍然擁有所有的結果,但是非同步呼叫將並行請求,而不是一個接一個地請求

async function getABC() {
  // Promise.all()允許我們同時傳送多個請求,型別是個陣列

  let results = await Promise.all([ getValueA, getValueB, getValueC ]); 

  return results.reduce((total,value) => total * value);
}

這樣一來,函式執行的時間就會少得多。getValueA 和 getValueC 呼叫在 getValueB 結束時已經完成,將有效地將執行時間減少到最慢的請求的時間(getValueB-4秒) ,而不是總和

Async/Await 中的錯誤處理

Async/Await 的另一個優點是,它允許我們在一個很好在 try/catch 塊中捕捉任何意外的錯誤。只需要像這樣包裝下Await:

async function doSomethingAsync(){
    try {
        // 這裡非同步可能會發送失敗
        let result = await someAsyncCall();
    }
    catch(error) {
        // 如果失敗了,通過catch捕捉到錯誤
    }  
}

Catch 子句將處理等待的非同步呼叫或者我們在 try 塊中編寫的任何其他失敗程式碼所引發的錯誤

如果情況需要,我們還可以在執行 async 函式時捕獲錯誤。因為所有的非同步函式都返回 Promises,我們可以簡單地包含一個。在呼叫 catch ()事件處理程式時。

async function doSomethingAsync(){
    // This async call may fail.
    let result = await someAsyncCall();
    return result;  
}

// 後面更用catch捕獲錯誤
doSomethingAsync().
    .then(successHandler)
    .catch(errorHandler);

總結

隨著 Async/Await 的加入,JavaScript 語言在程式碼可讀性和易用性方面取得了巨大的飛躍。編寫類似於常規同步函式的非同步程式碼的能力將受到初學者、 JavaScript 開發者和資深編碼者的青睞