首頁 >web前端 >js教程 >關於 JavaScript Promise 及其工作原理,您需要了解的一切

關於 JavaScript Promise 及其工作原理,您需要了解的一切

Patricia Arquette
Patricia Arquette原創
2024-12-17 03:33:25723瀏覽

Everything You Need to Know About JavaScript Promises and How They Work

現代 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 可以處於以下三種狀態之一:

  1. 待處理:這是第一步。承諾還沒兌現。

  2. Fulfilled:Promise 已成功完成,這意味著它已解決並且具有價值。

  3. 已拒絕: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中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn