JavaScript異步編程: 從回調地獄到async和await

原文鏈接:https://blog.hellojs.org/asynchronous-javascript-from-callback-hell-to-async-and-await-9b9ceb63c8e8

寫好一個優秀的web應用關鍵之一就是可以在一個頁面上做許多AJAX請求

前言

我們可以從一個簡單的例子的每個解決方式來思考JavaScript異步編程的進步

爲了做到這些,我們可以來做一個簡單的任務,這個任務是完成下面這些流程:

  1. 驗證用戶的名稱和密碼
  2. 獲取應用中用戶的角色
  3. 打印用戶訪問應用的時間

回調地獄的方式

最古老的解決這些問題的方式是通過一層套一層的的回調。這在過去是解決簡單的異步任務的優雅方式,但是呢,由於回調地獄的原因,並不能進行拓展升級。
在這裏插入圖片描述

用於解決三個簡單問題的代碼如下

const verifyUser = function(username, password, callback){
   dataBase.verifyUser(username, password, (error, userInfo) => {
       if (error) {
           callback(error)
       }else{
           dataBase.getRoles(username, (error, roles) => {
               if (error){
                   callback(error)
               }else {
                   dataBase.logAccess(username, (error) => {
                       if (error){
                           callback(error);
                       }else{
                           callback(null, userInfo, roles);
                       }
                   })
               }
           })
       }
   })
};

每個函數的調用都需要傳遞一個參數,這個參數也是一個函數,會接受前一個函數的返回值。

僅僅閱讀上面的句子都會使得太多人大腦麻木,而如果一個應用擁有數以百計的這種代碼的話,會對維護這些代碼的人造成極大的困擾,即使是這些人自己寫的這些代碼(不要高估明天的自己,明天的你不一定可以看懂今天的你寫的代碼)。

當你知道database.getRoles`也是一個嵌套回調的函數的時候,你會意識到這個例子變得更加複雜。

const getRoles = function (username, callback){
   database.connect((connection) => {
       connection.query('get roles sql', (result) => {
           callback(null, result);
       })
   });
};

這些代碼的問題,除了代碼難以維護以外,還有就是違背DRY原則(Do not repeat yourself)。例如,異常處理在每個函數中都被重複的進行(if else)並且調用的callback也在每個嵌套的function中調用。

JavaScript中的Promises

Promise是避免回調地獄的一個進步。此方法並沒有移除回調函數,但是將函數的調用連接起來並且簡化了代碼,使得代碼更易於閱讀。

在這裏插入圖片描述

使用了Promise的代碼如下

const verifyUser = function(username, password) {
   database.verifyUser(username, password)
       .then(userInfo => dataBase.getRoles(userInfo))
       .then(rolesInfo => dataBase.logAccess(rolesInfo))
       .then(finalResult => {
           //do whatever the 'callback' would do
       })
       .catch((err) => {
           //do whatever the error handler needs
       });
};

爲了做到這些,代碼中使用到的函數必須Promise化。我們看一下getRoles如何更新的返回一個Promise對象的。

const getRoles = function (username){
   return new Promise((resolve, reject) => {
       database.connect((connection) => {
           connection.query('get roles sql', (result) => {
               resolve(result);
           })
       });
   });
};

我們將此方法修改爲返回一個Promise對象,此對象需要傳入兩個回調函數,並且在Promise對象中執行操作。現在,resolvereject兩個回調函數會分別映射爲Promise.thenPromise.catch

你可能意識到getRoles方法的內部仍然是可能遭受地獄回調的,因爲database的方法並沒有返回一個Promise對象。如果database的連接方法也返回一個Promise對象,那麼getRoles就會像下面這樣

const getRoles = new function (userInfo) {
   return new Promise((resolve, reject) => {
       database.connect()
           .then((connection) => connection.query('get roles sql'))
           .then((result) => resolve(result))
           .catch(reject)
   });
};

方式3:Async/Await

JavaScript默認是異步的。這也可能是爲什麼JavaScript花了很長時間才使得代碼看起來像同步的。但是,遲到了總比沒有強。地獄回調在引入了Promise對象之後改善了很多。但是我們仍然需要傳遞迴調函數給Promise對象.then.catch

Promise給JavaScript帶來了最酷之一的改變。ECMAScript 2017在Promise之上已async和await表達式的方式帶來了語法糖。這些使得我們可以基於Promise的代碼看起來不像是異步的一樣,並且也不會阻塞主線程。

代碼如下:

const verifyUser = async function(username, password){
   try {
       const userInfo = await dataBase.verifyUser(username, password);
       const rolesInfo = await dataBase.getRoles(userInfo);
       const logStatus = await dataBase.logAccess(userInfo);
       return userInfo;
   }catch (e){
       //handle errors as needed
   }
};

await promise的操作僅僅允許在async修飾的函數中使用,async修飾的函數表示verifyUser必須定義爲異步函數

然而,這些簡單的改變就可以await任何Promise,並且不需要修改任何代碼。並且異步代碼可以想同步代碼一樣愉快的編碼了。

Async 一個等待已久的Promise的進步

異步函數是JavaScript中異步編程的下一個里程碑。他們會使得代碼更爲乾淨並且更爲簡單的維護。聲明一個函數爲async會確保函數返回的是一個Promise對象(即不需要單獨的定義返回的對象爲一個Promise對象),所以你並不需要爲此再擔心了。

我們如今爲什麼需要使用JavaScript中的async函數?

  1. 代碼會更爲整潔
  2. 異常處理更爲簡單,僅僅像在其他異步代碼使用try/catch即可
  3. 調試更爲簡單。在.then代碼塊設置斷點不會移動到下一個斷點,.then只會執行同步代碼。但是調試async函數就和調試同步代碼一樣

本文翻譯自https://blog.hellojs.org/asynchronous-javascript-from-callback-hell-to-async-and-await-9b9ceb63c8e8

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章