現代 Web 開發嚴重依賴非同步活動來實現響應式、互動式應用程式。無論是從 API 檢索資料、讀取檔案或運行計時器,這些進程都必須在背景運行,而不會凍結介面。 JavaScript 為您提供了一種可靠的方式來處理這些工作。本文涵蓋了您需要了解的有關 Promise 的所有信息,包括基本思想和高級功能,以開發無錯誤的非同步程式。
在本文中您將了解 —
什麼是 Promise?
為什麼要用 Promise?
Promise 如何運作?
處理 Promise
連鎖承諾
Promise 中的錯誤處理
進階 Promise 功能
有 Promise 的 JavaScript 執行流程(重要)
將 Promise 鏈轉換為非同步/等待
最佳實務與常見錯誤
什麼是承諾?
JavaScript 中的 Promise 相當於做出「承諾」在未來做某事。當你做出承諾時,你就是在說:「我保證稍後會給你結果。」這個結果可能是成功,也可能是失敗。
換句話說,Promise 是一個反映非同步操作最終成功(或失敗)及其結果值的物件。它允許您將處理程序與非同步操作的成功或失敗關聯起來,使您的程式碼更易於閱讀和維護。
為什麼要使用 Promise?
例如,在 JavaScript 中,耗時的操作(例如從伺服器檢索資料)通常是透過回調完成的。回調只是一個傳遞給另一個函數以在任務完成後執行的函數。例如,您可以使用回調來處理來自伺服器的資料。
但是,當有複雜的操作時,回呼的使用就變得相當混亂。這種混亂被稱為“回調地獄”,其中一個可以在另一個中進行回調,這使得程式碼不可讀且難以管理。
回調地獄範例:
fetchData((data) => { processData(data, (processedData) => { saveData(processedData, (result) => { console.log(result); }); }); });
如上所示,由於其深層嵌套結構(通常稱為“回調地獄”),此類程式碼在較大的程式碼庫中變得越來越難以閱讀和維護。
引入 Promise 來解決這個問題,透過允許以更易讀的方式進行鏈接,提供一種更乾淨、更有組織的方式來處理非同步任務。
基於承諾的方法:
fetchData((data) => { processData(data, (processedData) => { saveData(processedData, (result) => { console.log(result); }); }); });
這種方法扁平化了結構,使程式碼更具可讀性和可維護性。
承諾如何發揮作用?
JavaScript 中的 Promise 可以處於以下三種狀態之一:
待處理:這是第一步。承諾還沒兌現。
Fulfilled:Promise 已成功完成,這意味著它已解決並且具有價值。
已拒絕:Promise 未成功完成,並且帶有錯誤訊息。
基本文法
fetchData() .then(processData) .then(saveData) .then(console.log) .catch(console.error);
在此範例中,promise 在 1 秒後解析,並顯示訊息「Promise returned!」。 .then() 方法用於處理解析後的值。
處理承諾
使用 .then() 進行成功處理
.then()方法用於處理promise成功完成時發生的情況。它註冊函數(回調)以在承諾完成時運行。
const myPromise = new Promise((resolve, reject) => { setTimeout(() => { resolve("Promise resolved!"); }, 1000); }); myPromise.then(result => console.log(result));
使用 .catch() 進行錯誤處理
.catch()方法用於處理promise失敗時發生的情況。它註冊一個函數(回調)以在 Promise 被拒絕時運行。
myPromise.then(data => { console.log("Data received:", data); });
使用 .finally() 進行清理
.finally() 方法可以讓你在 Promise 完成後執行一些程式碼,無論成功與否。
myPromise.catch(error => { console.error("Error:", error); });
連結承諾
連結可讓您透過傳遞前一個任務的結果來順序執行任務。然後繼續進行next.then()。這允許您按順序處理多個非同步任務。
連結範例:
myPromise.finally(() => { console.log("Cleanup tasks"); });
此範例使用each.then()來處理流程中的每個步驟,從而實現清晰的資料流。這可以讓您看到一個階段的結果如何轉移到下一階段。
Promise 中的錯誤處理
Promise 透過讓它們將鏈向下傳遞給 .catch() 方法來解決,從而簡化了錯誤處理。這消除了在每個階段處理失敗的需要,使您的程式碼更清晰且更易於管理。
錯誤傳播範例:
fetch('https://api.example.com/user') .then(response => response.json()) .then(data => { console.log("Processed data:", data); return processData(data); }) .then(finalResult => { console.log("Final result:", finalResult); }) .catch(error => console.error("Error:", error));
如果 Promise 鏈中的任何一步失敗,錯誤將被 .catch() 區塊捕獲。這使得您可以輕鬆處理問題並保持程式碼順利運行。
高級 Promise 功能
1. Promise.all() 用於並行執行
Promise.all() 方法可讓您同時執行多個 Promise 並等待它們全部完成。如果所有承諾都得到履行,您將收到每項承諾的結果。如果任何承諾失敗,它會偵測到錯誤。
fetchData() .then(processData) .then(saveData) .catch(error => console.error("An error occurred:", error));
在此範例中,如果任何 Promise 失敗,則整個 Promise.all() 都會失敗。
2. Promise.race() 實現最快的 Promise
Promise.race() 方法傳回第一個完成的 Promise 的結果,無論成功或失敗。
fetchData((data) => { processData(data, (processedData) => { saveData(processedData, (result) => { console.log(result); }); }); });
在此範例中,無論哪個 Promise(fetchData1 或 fetchData2)先完成,其結果都會記錄到控制台。
3. Promise.allSettled() 用於處理所有結果
Promise.allSettled()方法等待你給它的所有promise都處於成功或失敗狀態,然後完成。然後傳回一個數組,其中包含每個承諾的結果。
fetchData() .then(processData) .then(saveData) .then(console.log) .catch(console.error);
在此範例中,Promise.allSettled() 等待 fetchData1() 和 fetchData2() 完成。然後它記錄每個承諾的狀態和結果(或錯誤)。這樣,您就可以看到每個 Promise 發生了什麼,無論它們是成功還是失敗。
4.Promise.any() 用來解決第一個成功的 Promise
Promise.any() 方法等待 Promise 清單中的第一個 Promise 被正確解析。如果至少有一個 Promise 解決,則 Promise.any() 方法將傳回該值。如果所有的 Promise 都被拒絕,這個方法將會拋出一個錯誤。
const myPromise = new Promise((resolve, reject) => { setTimeout(() => { resolve("Promise resolved!"); }, 1000); }); myPromise.then(result => console.log(result));
在此範例中,Promise.any() 等待第一個 Promise 成功解析。過程傳回第一個成功的 Promise 的結果,在本例中,promise2 的值為「Success A」。如果所有承諾都被拒絕,則執行 .catch() 區塊,並記錄錯誤訊息。當您想要收到第一個成功的 Promise 的結果而不必等待其餘的結果時,此策略非常有用。
帶有 Promise 的 JavaScript 執行流程(重要)
1. JavaScript 中的 Promise 在微任務佇列中運行,其優先權高於 setTimeout 等巨集任務。
這裡有一個例子來說明這一點:
myPromise.then(data => { console.log("Data received:", data); });
在此範例中:
console.log(2) 首先運行,因為它是常規同步操作。
console.log (6) 接下來運行,因為它也是同步的。
promise 的.then() 在 setTimeout 回調之前運行,因為 Promise 是微任務,具有更高的優先權,因此打印 3.
最後,setTimeout 回調運行,因為它是一個巨集任務並列印 4。
所以永遠記住,由於微任務佇列的優先權,promise's.then() 在 setTimeout 回調之前執行。
2. Promise 執行順序和具有多個 .then() 呼叫的微任務隊列
在 JavaScript 中,程式碼以特定順序執行:首先是同步程式碼,然後是微任務(如 Promise),最後是巨集任務(如 setTimeout)。
這裡有一個例子來解釋這一點:
fetchData((data) => { processData(data, (processedData) => { saveData(processedData, (result) => { console.log(result); }); }); });
在此範例中,同步程式碼首先運行,記錄 3、6、2、7 和 8。同步程式碼完成後,將處理微任務(then() 回呼),記錄 1 和 9。最後,巨集任務(來自 setTimeout)依延遲順序執行,記錄 21 (0ms) 和 13 (10ms)。這突顯了 JavaScript 的執行順序:同步程式碼 >微任務>巨集任務。
3. Promise 中的多個 Resolve 和 Reject 呼叫:只有第一個很重要
當您建立承諾時,第一個解決或拒絕的呼叫是唯一重要的。所有其他呼叫均被駁回。
這裡有一個例子來說明這一點:
fetchData() .then(processData) .then(saveData) .then(console.log) .catch(console.error);
在此範例中,promise 使用值 1 進行解析。第二個解析和拒絕呼叫將被忽略,因為 Promise 已透過第一個解析進行結算。
4. 在連續的 .then() 呼叫中連結 Promise 並處理值
當你連結 Promise 時,each.then() 會處理流程中的一個步驟。
const myPromise = new Promise((resolve, reject) => { setTimeout(() => { resolve("Promise resolved!"); }, 1000); }); myPromise.then(result => console.log(result));
在此範例中,Promise.resolve(1) 以值 1 開頭,但第一個 .then(() => 2) 傳回 2。下一個 .then(3) 被忽略,並且值 2 被傳遞。 .then((value) => value * 3) 將值乘以 3,結果是 6。 .then(Promise.resolve(4)) 不會改變值,最後,.then(console. log)logs 6。這演示了值如何透過鏈傳遞,非函數值被忽略。
5. 使用 .catch() 和 .finally() 處理的 Promise 鏈
myPromise.then(data => { console.log("Data received:", data); });
在此範例中,我們將多個 .then()、.catch() 和 .finally() 方法連結在一起,以展示如何處理 Promise 解析的不同階段。讓我們來分解一下:
finally() 沒有收到參數:
finally() 區塊執行清理程式碼,但不接受或傳遞任何值。它用於確保某些程式碼運行,無論承諾的結果如何。在finally()中回傳一個值不會影響promise:
如果您在finally()區塊中傳回一個值,它不會影響promise鍊或最終值。它在承諾解決/拒絕後執行,但不會修改結果。在finally()中拋出錯誤會導致拒絕:
如果你在finally()中拋出錯誤或回傳被拒絕的promise,將會導致promise鏈被拒絕,並帶有錯誤或拒絕原因。
myPromise.catch(error => { console.error("Error:", error); });
或
myPromise.finally(() => { console.log("Cleanup tasks"); });
- then() 和 catch() 的順序很重要 .then() 和 .catch() 可以按任何順序調用,但它們將始終返回 Promise 的最終狀態。當 .catch() 處理 Promise 時,任何後續的 .then() 將收到最終值。
範例:
fetchData((data) => { processData(data, (processedData) => { saveData(processedData, (result) => { console.log(result); }); }); });
將 Promise 鏈轉換為 Async/Await
Async/await 是一種使用 Promise 的方法,使程式碼更像以同步模式編寫的程式碼。經常使用的術語是“語法糖”,因為它提供了更直接、更清晰的非同步程式碼執行路徑。
fetchData() .then(processData) .then(saveData) .then(console.log) .catch(console.error);
將 Promise 與 Async/Await 結合
您可以使用 Promise.all() 將 Promise 與 async/await 結合以實現並行執行。
const myPromise = new Promise((resolve, reject) => { setTimeout(() => { resolve("Promise resolved!"); }, 1000); }); myPromise.then(result => console.log(result));
最佳實踐和常見錯誤
避免深層巢狀:使用連結或非同步/等待來保持程式碼平坦和可讀。
總是處理錯誤: 確保每個 Promise 鏈都有 a.catch() 或 try/catch 區塊。
明智地使用並行執行:僅當任務獨立但需要一起完成時才使用 Promise.all()。
結論
JavaScript Promise 是處理耗時操作(例如在伺服器上檢索資料)的最佳方法之一。它們甚至可以幫助您編寫更清晰、更易於維護的程式碼,而您所學的實踐將使您能夠充分利用非同步編碼。一旦你獲得了一些實務經驗並開始優雅地處理錯誤,Promise 將成為 JavaScript 的重要組成部分。
感謝您的閱讀!如果您覺得這篇文章有幫助,請隨時突出顯示、鼓掌、發表評論,甚至在 Twitter/X 和 LinkedIn 上與我聯繫,因為我非常感激並幫助保持此類內容免費!
以上是關於 JavaScript Promise 及其工作原理,您需要了解的一切的詳細內容。更多資訊請關注PHP中文網其他相關文章!

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

WebStorm Mac版
好用的JavaScript開發工具

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。