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
广告