[譯]JavaScript: Promises 介紹及為何 Async/Await 最終取得勝利

sunshine楊小咩發表於2019-03-01

原文地址:JavaScript: Promises and Why Async/Await Wins the Battle

非同步函式在JavaScript中有好有壞。好的一面是非同步函式是非阻塞的,因此很快 – 特別是在Node.js上下文中。缺點是處理非同步函式可能很麻煩,因為有時必須等待一個函式完成才能在進行下一次執行之前獲得“回撥”。

有一些方法可以發揮非同步函式呼叫的優勢並正確處理它們的執行,但其中一種方法遠遠優於其他方法(Spoiler:它是Async / Await)。在本文中,您將瞭解使用PromisesAsync/Await的來龍去脈,以及我們對兩者之間如何比較的看法。

Promises vs. Callbacks

作為JavaScript或Node.js開發人員,正確理解PromisesCallbacks之間的區別以及它們如何協同工作至關重要。

兩者之間存在微小但重要的差異。在每個Promise的核心,都有一個Callback解決某些資料(或錯誤),這些資料會被呼叫到Promise

回撥處理程式:

[譯]JavaScript: Promises 介紹及為何 Async/Await 最終取得勝利

呼叫validatePassword()功能:

[譯]JavaScript: Promises 介紹及為何 Async/Await 最終取得勝利

下面的程式碼片段顯示了驗證密碼的完整端到端檢查(它是靜態的,必須匹配“bambi”):

[譯]JavaScript: Promises 介紹及為何 Async/Await 最終取得勝利

程式碼註釋得非常好,但是,如果您感到困惑,catch只會在reject()promise呼叫時執行。由於密碼不匹配,我們呼叫reject(),因此“catch”錯誤並將其傳送到done()函式。

Promises

與傳統的基於回撥的方法相比,Promise為執行、組合和管理非同步操作提供了更簡單的替代方案。它們還允許你使用類似同步try / catch的方法處理非同步錯誤。

Promise還提供三種唯一的狀態

  1. Pendingpromise的結果尚未確定,因為將產生其結果的非同步操作尚未完成。
  2. Fulfilled – 非同步操作已完成,並且promise有值。
  3. Rejected – 非同步操作失敗,promise永遠不會實現。在被拒絕狀態下,promise有一個reason可以指示操作失敗的原因。

promisepending狀態時,它可以轉換為fulfilledrejected的狀態。然而,一旦promise得到fulfilledrejected,它將永遠不會過渡到任何其他狀態,其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()方法並選擇加入全域性錯誤處理程式,如下所示:

瀏覽器

[譯]JavaScript: Promises 介紹及為何 Async/Await 最終取得勝利

Node.js

[譯]JavaScript: Promises 介紹及為何 Async/Await 最終取得勝利

注意:以上兩個選項是確保捕獲錯誤的兩種方法。如果錯過了新增catch()方法,它將被程式碼吞噬。

Async/Await??

Async/Await允許我們編寫看起來是同步的非同步JavaScript。在本文的前幾部分中,您瞭解了Promises – 它應該簡化非同步流並避免回撥地獄但它沒有。

回撥地獄??

Callback-hell是一個用於描述以下場景的術語:

注意:舉個例子,這是一個API呼叫,可以從一個陣列中獲得4個特定使用者。

[譯]JavaScript: Promises 介紹及為何 Async/Await 最終取得勝利

這樣的程式碼這很難看,也佔用了大量的空間。Async/Await是JavaScript的最新和最好的東西,它允許我們不僅避免回撥地獄,而且確保我們的程式碼乾淨並且錯誤被正確捕獲。我發現Async/Await最令人著迷的是它構建在Promises之上(非阻塞等),並且允許程式碼可讀並且就像讀取它是同步的一樣。這就是關鍵所在。

注意:以下是一組API呼叫的示例,用於從一個陣列中檢索4個使用者,超過一半的程式碼行:

[譯]JavaScript: Promises 介紹及為何 Async/Await 最終取得勝利

程式碼這樣寫比較優雅,對嗎??

因為Async/Await是建立在Promises之上的,所以你甚至可以在關鍵字await使用Promise.all()

[譯]JavaScript: Promises 介紹及為何 Async/Await 最終取得勝利

注意:由於同步特性,Async/await稍微慢一些。連續多次使用它時應該小心,因為await關鍵字會停止執行後面的所有程式碼 – 就像在同步程式碼中一樣。

如何開始使用Async/Await??

使用Async/Await非常容易理解和使用。實際上,它可以在最新版本的Node.js中本地使用,並且正在迅速融入瀏覽器。現在,如果你想在客戶端使用它,你需要使用Babel。

非同步Async

讓我們從async關鍵字開始。它可以放在function之前,如下所示:

[譯]JavaScript: Promises 介紹及為何 Async/Await 最終取得勝利

等待Await

關鍵字await使JavaScript等待promise繼續並返回其結果。如下所示:

[譯]JavaScript: Promises 介紹及為何 Async/Await 最終取得勝利

完整的例子

[譯]JavaScript: Promises 介紹及為何 Async/Await 最終取得勝利

為什麼Async/Await更好??

現在我們已經瞭解了PromisesAsync/Await所提供的很多內容,讓我們回顧一下為什麼Stream認為Async/Await是程式碼庫的最佳選擇。

  1. Async/Await允許使用更少的程式碼行,更少的輸入和更少的錯誤,提供簡潔明瞭的程式碼庫。最終,它使複雜的巢狀程式碼再次可讀。
  2. 使用try/catch處理錯誤(在一處,而不是在每個呼叫中)
  3. 錯誤堆疊是有意義的,而不是從Promises收到的模糊錯誤,它們很大並且很難找到錯誤發生的位置。最重要的是,錯誤指向錯誤發生的函式。

最後的想法?

可以說Async/Await是過去幾年中新增到JavaScript中的最強大的功能之一。

花了不到一天的時間來理解語法,看看我們的程式碼庫在這方面是多麼糟糕。將我們所有基於Promise的程式碼轉換為Async/Await總共花費了大約兩天時間,這實際上是一個完全重寫 – 這只是為了說明使用Async/Await時需要更少的程式碼。

相關文章