JavaScript - Promise 鏈式呼叫



JavaScript 中的Promise 鏈式呼叫即使使用單個 Promise 也能處理多個相關的非同步操作。單個 Promise 處理單個非同步操作,而 Promise 鏈式呼叫允許您建立一個 Promise 序列。其中,一個 Promise 的成功或失敗都會觸發下一個 Promise 的執行。這使您可以處理多個非同步操作。

在 JavaScript 中,我們可以使用 Promise() 建構函式生成 Promise 程式碼,並使用 then() 方法使用它。它處理單個非同步操作。要處理多個非同步操作,我們需要使用多個 Promise,如下例所示。

示例

在下面的程式碼中,我們定義了 promise1,它在 1 秒後被 resolved。我們還定義了全域性變數“data”。

之後,我們使用 then() 方法使用 promise1,並在回撥函式中將 Promise 的返回值儲存在 data 中。

接下來,我們定義了 promise2,它在 2 秒後被 resolved。接下來,我們使用 then() 方法和 promise2,並在回撥函式中使用了“data”變數。

<html>
<body>
   <div id = "output"> </div>
   <script>
      let output = document.getElementById("output");
      var data;
      // First promise
      let promise1 = new Promise((resolve, reject) => {
         setTimeout(() => {
            resolve(10);
         }, 1000);
      });
      promise1.then((value) => {
         data = value; // Stroing value into the data
         output.innerHTML += "The promise1 is resolved and data is: " + data + "<br>";
      });

      // Second promise
      let promise2 = new Promise((resolve, reject) => {
         setTimeout(() => {
            resolve(20);
         }, 2000);
      });
      promise2.then((value) => {
         data = data * value; // Using the data from the first promise
         output.innerHTML += "The promise2 is resolved and data is: " + value + "<br>";
         output.innerHTML += "The final value of the data is: " + data + "<br>";
      });
   </script>
</body>
</html>

輸出

The promise1 is resolved and data is: 10
The promise2 is resolved and data is: 20
The final value of the data is: 200

在上面的示例中,我們建立了兩個不同的 Promise 來對 promise1 返回的資料執行多個操作。

這增加了程式碼的複雜性並降低了可讀性。

這時,Promise 鏈式呼叫就派上用場了。

JavaScript Promise 鏈式呼叫

JavaScript 中 Promise 鏈式呼叫的概念允許您使用單個 Promise 執行多個相關的非同步操作。

您可以在使用 Promise 時使用多個 then() 方法來執行多個非同步操作。

語法

JavaScript 中 Promise 鏈式呼叫的語法如下:

Promise
   .then(callback);
   .then(callback);
   ...
   .then(callback);

在上述語法中,我們使用了多個 then() 方法來處理多個非同步操作。每個 then() 方法都執行單個回撥函式。

示例

在下面的程式碼中,我們定義了 promise1。之後,我們使用 Promise 鏈來執行多個非同步操作。

在第一個 then() 方法中,我們在乘以 2 後返回該值。在下一個 then() 方法中,我們列印更新後的值,並在乘以 2 後返回新值。我們在第三個 then() 方法中也執行了類似的操作。

<html>
<body>
   <div id = "output"> </div>
   <script>
      let output = document.getElementById("output");
      const promise1 = new Promise((resolve, reject) => {
         resolve(2);
      });

      // Promise chaining
      promise1
      .then((value) => {
         output.innerHTML = "The square of 2 is " + value * 2 + "<br>";
         return value * 2; // Returning a promise for next then() method
      })
      .then((value) => {
         output.innerHTML += "The square of 4 is " + value * 2 + "<br>";
         return value * 2;
      })
      .then((value) => {
         output.innerHTML += "The square of 8 is " + value * 2 + "<br>";
      });
   </script>
</body>
</html>

輸出

The square of 2 is 4
The square of 4 is 8
The square of 8 is 16

多個 Promise 處理程式

您還可以使用多個 Promise 處理程式來使用單個 Promise。但是,如果您使用多個 Promise 處理程式,則它不被視為 Promise 鏈式呼叫。

示例

在下面的程式碼中,我們建立了 promise1。

之後,我們使用多個 Promise 處理程式來使用該 Promise。每個 Promise 處理程式都分別解決該 Promise。

<html>
<body>
   <div id = "output"> </div>
   <script>
      let output = document.getElementById("output");
      const promise1 = new Promise((resolve, reject) => {
         resolve(2);
      });

      promise1
      .then((value) => {
         output.innerHTML += "Inside the first promise handler. <br>";
         return value * 2;
      })

      promise1
      .then((value) => {
         output.innerHTML += "Inside the second promise handler. <br>";
         return value * 2;
      })

      promise1
      .then((value) => {
         output.innerHTML += "Inside the third promise handler. <br>";
         return value * 2;
      })
   </script>
</body>
</html>

輸出

Inside the first promise handler.
Inside the second promise handler.
Inside the third promise handler.

使用 Promise 鏈式呼叫進行錯誤處理

您可以使用 catch() 方法與 Promise 鏈式呼叫結合來處理錯誤。

如果您在所有 then() 方法之後最後使用 catch() 方法,它會捕獲任何 then() 方法中的錯誤並進行處理。如果您在 then() 方法之間使用 catch() 方法,它會捕獲之前 then() 方法中的錯誤。

讓我們透過下面的示例來了解它。

示例

在下面的程式碼中,我們定義了 Promise 並將其 rejected。

之後,我們使用 Promise 鏈式呼叫來使用該 Promise。我們使用了兩個 then() 方法和一個在所有 then() 方法之後的 catch() 方法。

在輸出中,您可以看到,由於我們 rejected 了 Promise,因此控制權進入了 catch() 方法。

<html>
<body>
   <div id = "output"> </div>
   <script>
      let output = document.getElementById("output");
      const promise1 = new Promise((resolve, reject) => {
         reject("There is an error.");
      });

      promise1
      .then((value) => {
         output.innerHTML += "The returned value is: " + value + "<br />";
         return value + " Everything is fine!";
      })
      .then((value) => {
         output.innerHTML += value;
      })
      .catch((error) => {
         output.innerHTML += error;
      });
   </script>
</body>
</html>

輸出

There is an error.

返回 Promise

當您從 then() 方法返回值時,它會預設返回 Promise 並使用返回值解析它,因為它是一個非同步方法。

但是,您可以手動返回 Promise 來拒絕 Promise 或執行任何其他操作。

示例

在下面的程式碼中,我們定義了 promise1,並在回撥函式中使用了 setTimeOut() 方法。

之後,我們使用多個 then() 方法使用該 Promise。在每個 then() 方法中,我們都返回一個新的 Promise。

如果只返回`then()`方法的值,它會返回一個立即解析的Promise。但如果需要新增一些延遲,則可以從`then()`方法返回Promise。

<html>
<body>
   <div id = "output"> </div>
   <script>
      let output = document.getElementById("output");
      const promise1 = new Promise((resolve, reject) => {
         setTimeout(() => {
            resolve("Stage 1");
         }, 500);
      });

      promise1
      .then((value) => {
         output.innerHTML += value + "<br />";
         return new Promise((resolve, reject) => {
            setTimeout(() => {
               resolve("Stage 2");
            }, 1000);
         });
      })
      .then((value) => {
         output.innerHTML += value + "<br />";
         return new Promise((resolve, reject) => {
            setTimeout(() => {
               resolve("Stage 3");
            }, 200);
         });
      })
      .then((value) => {
         output.innerHTML += value + "<br />";
         output.innerHTML += "Finished";
      })
   </script>
</body>
</html>

輸出

Stage 1
Stage 2
Stage 3
Finished

將巢狀回撥函式轉換為Promise鏈

在“JavaScript-callbacks”章節中,你學習了巢狀回撥函式。由於其複雜的語法,它也被稱為回撥地獄。

在這裡,我們將學習如何將回調地獄轉換為Promise鏈,使其更易於閱讀。

讓我們來看一個巢狀回撥函式的例子。

巢狀回撥函式

示例

在下面的程式碼中,`updateData()`函式將資料作為第一個引數,回撥函式作為第二個引數。

`updateData()`函式在1000毫秒後呼叫回撥函式,並將資料作為引數傳遞。

接下來,我們呼叫了`updateData()`函式,並將10作為第一個引數,匿名函式作為回撥函式。

回撥函式將結果值儲存到p中,方法是將1新增到’num1’值。

接下來,我們在回撥函式內部呼叫`updateData()`函式。我們也傳遞了資料和回撥函式作為引數。這樣,我們就定義了巢狀回撥函式。

<html>
<body>
   <div id = "output"> </div>
   <script>
      let output = document.getElementById("output");
      output.innerHTML += "Wait for updating the data...<br>";
      //    Callback hell
      function updateData(data, callback) {
         setTimeout(() => {
            callback(data);
         }, 1000);
      }
      updateData(10, function (num1) {
         let p = 1 + num1;
         updateData(30, function (num2) {
            let q = 1 + num2;
            updateData("The numeric value is: " + (p + q), function (answer) {
               output.innerText += answer;
            });
         });
      });
   </script>
</body>
</html>

輸出

Wait for updating the data...
The numeric value is: 42

現在,讓我們學習如何將上面的例子轉換為Promise鏈。

將巢狀回撥函式轉換為Promise鏈

示例

在下面的程式碼中,`updateData()`函式返回單個Promise。

之後,我們使用了Promise鏈,它是上面示例中定義的回撥地獄的替代方案。

<html>
<body>
   <div id = "output"> </div>
   <script>
      let output = document.getElementById("output");
      output.innerHTML += "Wait for updating the data...<br>";
      function updateData(data) {
         return new Promise((resolve, reject) => {
            setTimeout(() => {
               resolve(data);
            }, 1000);
         });
      }

      updateData(10)
      .then((num1) => {
         let p = 1 + num1;
         return updateData(p);
      })
      .then((num2) => {
         let q = 31;
         return updateData("The final value is: " + (num2 + q));
      })
      .then((res) => {
         output.innerText += res;
      });
   </script>
</body>
</html>

輸出

Wait for updating the data...
The final value is: 42

Promise鏈的即時示例

在即時開發中,您可以使用Promise鏈來獲取資料並在資料上執行操作。

示例

在下面的程式碼中,當用戶點選“獲取資料”按鈕時,它會呼叫`fetchData()`函式。

在`fetchData()`函式中,我們使用了`fetch()` API從API獲取資料。

之後,我們使用`then()`方法將資料轉換為JSON。

接下來,我們再次使用`then()`方法列印JSON資料。

<html>
<body>  
   <button onclick = "fetchData()"> Fetch Data </button>
   <div id = "output"> </div>
   <script>
      let output = document.getElementById("output");
      function fetchData() {
         fetch('https://jsonplaceholder.typicode.com/todos/1')
         .then(response => response.json()) // Promise chaining
         .then((data) => {
            output.innerHTML += "The data is - " + JSON.stringify(data);
         })
      }
   </script>
</body>
</html>

輸出

Real-time Examples of Promise Chaining
廣告