原文地址:JavaScript: Promises and Why Async/Await Wins the Battle
非同步函式在JavaScript中有好有壞。好的一面是非同步函式是非阻塞的,因此很快 – 特別是在Node.js上下文中。缺點是處理非同步函式可能很麻煩,因為有時必須等待一個函式完成才能在進行下一次執行之前獲得“回撥”。
有一些方法可以發揮非同步函式呼叫的優勢並正確處理它們的執行,但其中一種方法遠遠優於其他方法(Spoiler:它是Async / Await)。在本文中,您將瞭解使用Promises和Async/Await的來龍去脈,以及我們對兩者之間如何比較的看法。
Promises vs. Callbacks
作為JavaScript或Node.js開發人員,正確理解Promises
和Callbacks
之間的區別以及它們如何協同工作至關重要。
兩者之間存在微小但重要的差異。在每個Promise
的核心,都有一個Callback
解決某些資料(或錯誤),這些資料會被呼叫到Promise
。
回撥處理程式:
呼叫validatePassword()功能:
下面的程式碼片段顯示了驗證密碼的完整端到端檢查(它是靜態的,必須匹配“bambi”):
程式碼註釋得非常好,但是,如果您感到困惑,catch
只會在reject()
從promise
呼叫時執行。由於密碼不匹配,我們呼叫reject()
,因此“catch”
錯誤並將其傳送到done()
函式。
Promises
與傳統的基於回撥的方法相比,Promise
為執行、組合和管理非同步操作提供了更簡單的替代方案。它們還允許你使用類似同步try / catch
的方法處理非同步錯誤。
Promise還提供三種唯一的狀態:
Pending
–promise
的結果尚未確定,因為將產生其結果的非同步操作尚未完成。Fulfilled
– 非同步操作已完成,並且promise
有值。Rejected
– 非同步操作失敗,promise
永遠不會實現。在被拒絕狀態下,promise
有一個reason
可以指示操作失敗的原因。
當promise
在pending
狀態時,它可以轉換為fulfilled
或rejected
的狀態。然而,一旦promise
得到fulfilled
或rejected
,它將永遠不會過渡到任何其他狀態,其value
或失敗原因不會改變。
缺點?
Promise
不做的一件事是解決所謂的“回撥地獄”(原文:The one thing promises don’t do is solve what is called “callback hell”, which is really just a series of nested function calls. ),“回撥地獄”實際上只是一系列巢狀函式呼叫。當然,對於一個呼叫沒關係。但是對於多個呼叫,您的程式碼將會難以閱讀和維護。
在Promises中迴圈?
為了避免使用JavaScript進行深度巢狀回撥,假設可以簡單地遍歷Promises
,將結果返回給物件或陣列,並在完成後停止。不幸的是,這並不容易; 由於JavaScript的非同步特性,如果迴圈遍歷每個Promise
,在程式碼完成時不會呼叫“done”
事件。
處理這種情況的正確方法是使用Promise.all()
。這個函式在它被標記為已完成之前等待所有的Fulfillments
(或第一次rejection
)。
錯誤處理?
使用多個巢狀的Promise
呼叫進行錯誤處理就像蒙著眼睛的駕駛汽車一樣。祝你好運找出哪個Promise
犯了錯誤。你最好的選擇是完全刪除catch()
方法並選擇加入全域性錯誤處理程式,如下所示:
瀏覽器:
Node.js:
注意:以上兩個選項是確保捕獲錯誤的兩種方法。如果錯過了新增catch()
方法,它將被程式碼吞噬。
Async/Await??
Async/Await
允許我們編寫看起來是同步的非同步JavaScript。在本文的前幾部分中,您瞭解了Promises
– 它應該簡化非同步流並避免回撥地獄但它沒有。
回撥地獄??
Callback-hell
是一個用於描述以下場景的術語:
注意:舉個例子,這是一個API呼叫,可以從一個陣列中獲得4個特定使用者。
這樣的程式碼這很難看,也佔用了大量的空間。Async/Await
是JavaScript的最新和最好的東西,它允許我們不僅避免回撥地獄,而且確保我們的程式碼乾淨並且錯誤被正確捕獲。我發現Async/Await
最令人著迷的是它構建在Promises
之上(非阻塞等),並且允許程式碼可讀並且就像讀取它是同步的一樣。這就是關鍵所在。
注意:以下是一組API呼叫的示例,用於從一個陣列中檢索4個使用者,超過一半的程式碼行:
程式碼這樣寫比較優雅,對嗎??
因為Async/Await
是建立在Promises之上的,所以你甚至可以在關鍵字await
使用Promise.all()
:
注意:由於同步特性,Async/await
稍微慢一些。連續多次使用它時應該小心,因為await
關鍵字會停止執行後面的所有程式碼 – 就像在同步程式碼中一樣。
如何開始使用Async/Await??
使用Async/Await
非常容易理解和使用。實際上,它可以在最新版本的Node.js中本地使用,並且正在迅速融入瀏覽器。現在,如果你想在客戶端使用它,你需要使用Babel。
非同步Async
讓我們從async
關鍵字開始。它可以放在function
之前,如下所示:
等待Await
關鍵字await
使JavaScript等待promise
繼續並返回其結果。如下所示:
完整的例子
為什麼Async/Await更好??
現在我們已經瞭解了Promises
和Async/Await
所提供的很多內容,讓我們回顧一下為什麼Stream認為Async/Await
是程式碼庫的最佳選擇。
Async/Await
允許使用更少的程式碼行,更少的輸入和更少的錯誤,提供簡潔明瞭的程式碼庫。最終,它使複雜的巢狀程式碼再次可讀。- 使用
try/catch
處理錯誤(在一處,而不是在每個呼叫中) - 錯誤堆疊是有意義的,而不是從
Promises
收到的模糊錯誤,它們很大並且很難找到錯誤發生的位置。最重要的是,錯誤指向錯誤發生的函式。
最後的想法?
可以說Async/Await
是過去幾年中新增到JavaScript中的最強大的功能之一。
花了不到一天的時間來理解語法,看看我們的程式碼庫在這方面是多麼糟糕。將我們所有基於Promise
的程式碼轉換為Async/Await
總共花費了大約兩天時間,這實際上是一個完全重寫 – 這只是為了說明使用Async/Await時需要更少的程式碼。