您是否曾經感覺自己在咖啡店排隊等候 JavaScript 來取拿鐵咖啡?非同步程式設計常常給人這樣的感覺——同時處理多個訂單可能會讓您陷入等待。幸運的是,Promises 和 async/await 等工具可確保流程保持平穩高效,讓您的程式碼繼續運作而不會出現延遲。
在本指南中,我們將詳細介紹 Promise 的工作原理、引入 async/await 的原因,以及它如何簡化非同步程式碼的編寫。無論您是試圖掌握這些概念的初學者,還是想清楚何時使用每種方法,本文都將幫助您掌握基礎知識。
Promise 是 JavaScript 中處理非同步運算的基本概念。從本質上講,Promise 代表了現在、稍後或永遠可用的值。將其視為包裹的追蹤號碼:雖然您還沒有收到包裹,但追蹤號碼讓您確信包裹正在運送途中(或讓您知道是否出現問題)。
基於「現在、以後或永遠」的敘述,Promise 實際上在以下三種狀態之一中運行:
建立和使用 Promise 涉及一個簡單的 API。以下是定義 Promise 的方法:
const fetchData = new Promise((resolve, reject) => { setTimeout(() => { const data = { id: 1, name: "JavaScript Basics" }; resolve(data); // Simulates a successful operation // reject("Error: Unable to fetch data"); // Simulates a failure }, 1000); });
要處理結果,您可以將 .then()、.catch() 和 .finally() 方法連結到 Promise 物件:
fetchData .then((data) => { console.log("Data received:", data); }) .catch((error) => { console.error(error); }) .finally(() => { console.log("Operation complete."); });
當 Promise 解析成功時執行 then() 方法中的回呼。 .catch() 方法中的回呼在 Promise 解析失敗時執行,finally() 方法中的回呼在 Promise 解析後執行,無論解析結果為何。
Promise 為深層嵌套的回調(通常稱為「回調地獄」)提供了一種更乾淨的替代方案。 Promise 允許鏈接,而不是堆疊回調,從而使操作流程更易於遵循:
doTask1() .then((result1) => doTask2(result1)) .then((result2) => doTask3(result2)) .catch((error) => console.error("An error occurred:", error));
如果使用傳統回呼來寫相同的程式碼,它會是什麼樣子:
doTask1((error1, result1) => { if (error1) { console.error("An error occurred:", error1); return; } doTask2(result1, (error2, result2) => { if (error2) { console.error("An error occurred:", error2); return; } doTask3(result2, (error3, result3) => { if (error3) { console.error("An error occurred:", error3); return; } console.log("Final result:", result3); }); }); });
令人困惑,不是嗎?這就是為什麼 Promise 在引入時改變了 JavaScript 編碼標準。
雖然 Promises 大大改進了傳統的回呼函數,但它們也面臨著自己獨特的挑戰。儘管有這些好處,但它們在複雜的場景中可能會變得笨拙,導致程式碼冗長和調試困難。
即使使用 .then() 鏈接,Promise 在處理多個非同步操作時也會導致程式碼混亂。例如,使用 .then() 區塊管理順序操作和使用 .catch() 進行錯誤處理可能會讓人感覺重複且難以理解。
const fetchData = new Promise((resolve, reject) => { setTimeout(() => { const data = { id: 1, name: "JavaScript Basics" }; resolve(data); // Simulates a successful operation // reject("Error: Unable to fetch data"); // Simulates a failure }, 1000); });
雖然比巢狀回呼更清晰,但連結語法仍然很冗長,特別是在需要詳細的自訂錯誤處理邏輯時。此外,忘記在鏈的末尾添加 .catch() 可能會導致靜默失敗,從而使調試變得棘手。
此外,Promise 中的堆疊追蹤不如同步程式碼中的堆疊追蹤那麼直觀。發生錯誤時,堆疊追蹤可能無法清楚指示非同步流程中問題的根源。
最後,雖然 Promise 有助於減少回調地獄,但當任務相互依賴時,它們仍然會導致複雜性。嵌套的 .then() 區塊可以在某些用例中重新出現,帶回一些它們本來要解決的可讀性挑戰。
隨著 ES2017 (ES8) 中 async/await 的引入,JavaScript 中的非同步程式設計取得了巨大的飛躍。 async/await 建構在 Promises 之上,允許開發人員編寫外觀和行為更像同步程式碼的非同步程式碼。這使其成為真正的遊戲規則改變者,可以提高可讀性、簡化錯誤處理並減少冗長。
Async/await 是一種旨在使非同步程式碼更易於理解和維護的語法。
async 關鍵字用來宣告一個總是傳回 Promise 的函數。在此函數中,await 關鍵字暫停執行,直到 Promise 解決或拒絕。這會產生線性且直觀的流程,即使對於複雜的非同步操作也是如此。
以下是 async/await 如何簡化您在上面看到的相同程式碼範例的範例:
fetchData .then((data) => { console.log("Data received:", data); }) .catch((error) => { console.error(error); }) .finally(() => { console.log("Operation complete."); });
Async/await 消除了對 .then() 鏈的需要,允許程式碼按順序流動。這使得遵循邏輯變得更容易,特別是對於需要依序執行的任務。
對於 Promise,必須使用 .catch() 在鏈的每個層級捕獲錯誤。另一方面,Async/await 使用 try/catch 整合錯誤處理,減少重複並提高清晰度。
Async/await 產生比 Promise 更直覺的堆疊追蹤。當發生錯誤時,追蹤會反映實際的函數呼叫層次結構,從而減少偵錯的麻煩。總的來說,async/await 感覺更“自然”,因為它與同步程式碼的編寫方式一致。
正如您已經看到的,Async/await 在可讀性方面表現出色,尤其是對於順序操作。 Promise 及其 .then() 和 .catch() 連結很快就會變得冗長或複雜。相比之下,非同步/等待程式碼更容易理解,因為它模仿同步結構。
Promise 仍然有一席之地,特別是對於並發任務。 Promise.all() 和 Promise.race() 等方法對於平行運行多個非同步操作更有效。 Async/await 也可以處理這種情況,但需要額外的邏輯才能達到相同的結果。
const fetchData = new Promise((resolve, reject) => { setTimeout(() => { const data = { id: 1, name: "JavaScript Basics" }; resolve(data); // Simulates a successful operation // reject("Error: Unable to fetch data"); // Simulates a failure }, 1000); });
雖然使用單一 .catch() 進行集中式錯誤處理對於 Promises 的線性鏈效果很好,但建議對跨鏈的不同錯誤類型使用分佈式 .catch 調用,以獲得最佳可讀性。
另一方面,try/catch 區塊為處理錯誤提供了更自然的結構,特別是在處理順序任務時。
就效能而言,async/await 本質上等同於 Promises,因為它是建立在 Promises 之上的。然而,對於需要並發的任務,Promise.all() 可以更有效率,因為它允許多個 Promise 並行執行,如果任何 Promise 拒絕,則快速失敗。
如果您的任務涉及大量並發操作,例如同時從多個 API 獲取數據,Promise 很可能是更好的選擇。如果您的非同步程式碼不涉及大量鏈接,那麼 Promise 也非常適合這種情況,因為它很簡單。
另一方面,async/await 在需要順序執行大量任務或優先考慮可讀性和可維護性的情況下表現出色。例如,如果您有一系列相關操作,例如獲取數據、轉換數據和保存數據,則 async/await 提供乾淨且同步的結構。這使得追蹤操作流程變得更加容易,並透過 try/catch 區塊簡化了集中式錯誤處理。 Async/await 對於初學者或優先考慮可讀程式碼的團隊特別有用。
JavaScript 提供了兩個強大的工具來管理非同步操作:Promises 和 async/await。 Promise 徹底改變了開發人員處理非同步任務的方式,解決了回調地獄和啟用連結等問題。 Async/await 建立在 Promises 的基礎上,提供了更清晰的語法,感覺更自然和直觀,特別是對於順序任務。
既然您已經探索了這兩種方法,您就可以選擇最適合您需求的方法了。嘗試將基於 Promise 的函數轉換為 async/await 並觀察可讀性的差異!
有關更多信息,請查看 MDN Promise 文件或嘗試互動式編碼沙箱!
以上是Async/Await 與 Promises:JavaScript 初學者簡單指南的詳細內容。更多資訊請關注PHP中文網其他相關文章!