前言
前端同學們可能都知道 async
和 await
的使用,當被面試官問到 async
和 await
的是什么?或者說一說你對 async
、await
的理解?如果我們還是僅僅去闡述我是如何使用的就顯得格外的蒼白無力。今天博主就來帶大家進一步認識我們的 async
和 await
。首先來說由于瀏覽器/引擎負責解釋和執行JavaScript的主線程是單線程,同步執行一個耗時較大的任務會導致阻塞。異步執行代碼就是來解決阻塞問題,但會帶來順序的不確定性(多個異步執行過程的不確定性)
回調函數
如果說這些異步之間不會相互依賴或者不會因為順序不確定收到影響是么有關系的,但是如果我們一些邏輯需要依賴某個異步的返回值那么這個時候我們就需要保證執行順序,這時候我們就需到了回調函數,舉個例子:fun2需要fun1異步執行后的結果,fun3又依賴于 fun2 異步執行結束后的結果,所以我們通過回調函數來保證執行順序。
Promise
我們能夠看出上面的代碼中,多級嵌套的回調函數,弊端很多:不直觀,強耦合,回調的不確定性,不利于維護與復用,我們上面僅僅是非常簡單的一個場景,那如果我們有很復雜的需求的話非常多的回調嵌套就容易產生回調地獄。為了解決這個問題,Promise 出現了,它將這種回調函數的嵌套改為鏈式調用。并用then保證執行順序。Promise還能保證每次返回的都是一個新的Promise對象,所以代碼一定被異步執行。
我們發現上面的代碼可讀性就明顯略勝一籌,通過這樣一個鏈式的結構讓我們清晰的看出誰依賴誰,對我們的后期代碼維護更加友好,但這也僅僅是我們的一個小小的案例,如果是復雜的業務的話我們一直鏈式其實可讀性也會隨之增多也變得費勁。
async、await
Promise 是為了避免回調地獄的,但是 Promise 還是不夠簡明,語義化,隨著業務邏輯的增加可能會有超級多 then 于是有了async和await,先從字面意思來理解: async 是“異步”的簡寫,而 await 可以認為是 async wait 的簡寫,所以應該很好理解 async 用于申明一個 function 是異步的,而 await 用于等待一個異步方法執行完成。async 和 await 是es7提供的語法,是 es7最重要特性之一,相比于es6的promise ,具有更高的代碼可讀性,因此也被稱為promise 的語法糖。它把異步執行的代碼寫得像同步代碼那樣直觀。
async
快速創建一個異步函數,我們的 async 包裹后的函數就自動轉化成一個異步的 Promise 下面我們來證實一下
按照我們的理解,其實上面的log應該打印的是我們的 return 出來的幾何心涼對吧,但是他打印出來的是一個 Promise 等同于下面的代碼,當然這種只是我們的函數中僅僅是返回一個結果,但如果我們涉及到業務或者負責內容的時候還是需要我們去書寫 promise 的,這點也比較雞肋。
<body><script>function fun1() {let p = new Promise(rv => {rv('幾何心涼')})return p}console.log(fun1())</script>
</body>
await
await 并不是將異步代碼轉化成同步,他只是改變了調用方式,讓我們從調用的代碼層面看著相似于同步,從而增加代碼的可讀性;而且 await 也不是在任何地方都可以用的,它只能使用在 async 和模塊中使用,首先讓我們來看一下在 async 中的使用;
說明: 調用異步函數時,在函數前直接使用await對函數進行調用,一旦遇到await就會立即返回一個pending狀態的Promise對象,暫時返回執行代碼的控制權,使得函數外的代碼得以繼續執行,這是保證非阻塞的部分。他會等待 Promise 函數返回結果,可以通過變量來接收這個結果;既然是等待了那不就是說明阻塞了么?其實并不是我們可以看到圖中的 fun4 前面加上了 async 就說明我們的 fun4 是異步的了,異步代碼就不會阻塞后面的代碼執行,而我們的 await 是在異步函數中去控制了依賴項的執行順序。
當然在我們的 Promise 中我們出現了報錯我們是通過 catch 然后去處理的,在我們的 await 中我們又該如何處理呢?當然我們可以通過我們同步的辦法 try-catch 來處理異常
剛才我們上面提到說我們的 await 可以在模塊中使用,下面我們來看一下如何在模塊中使用:上面的例子如果我們直接把黃色框中的內容放到我們的外部來;執行會報錯,但是我們把 script 的類型改成模塊就可以了;注意如果改成模塊的話,我們 await 只能在最外層作用域中使用;
小結
我們的代碼中會通過異步的方式來防止代碼阻塞,當我們多個異步之間有相互的執行結果依賴的時候我們就需要鎖定他們的執行順序,早起的回調函數為我們解決了不確定順序的問題,但是由于大批量的使用回調函數,造成代碼的可讀性、維護性非常低,后面出現了我們的 Promise 通過 then 進行鏈式結構,提高了可讀性,但是隨著業務的復雜度增加我們的鏈式可讀性也會隨之降低,所以es7出現了 async、await 改變了調用的方式,讓調用呈現出來的代碼相似于同步,進而提升代碼可讀性,async: 通過 async 聲明的函數,它的返回值會自動包裝為 Promise,它聲明的異步函數中可以通過 await 去調用其他的異步函數,await: 讓我們通過同步的方式去調用異步函數,讓我們的代碼可讀性更高,await 的使用位置有兩個,一個是我們 async 聲明的異步函數中,一個是我們的 js 模塊的最外層作用域中;