JavaScript - Promise 链
JavaScript 中的 Promise 链 即使使用单个 Promise,也可以处理多个相关的异步操作。单个 Promise 只能处理单个异步操作,而 Promise 链允许您创建一系列 Promise。一个 Promise 的成功或失败都会触发下一个 Promise 的执行。这使您能够处理多个异步操作。
在 JavaScript 中,我们可以使用 Promise() 构造函数生成 Promise 代码,并使用 then() 方法进行处理。它处理单个异步操作。要处理多个异步操作,我们需要使用多个 Promise,如下例所示。
示例
在下面的代码中,我们定义了 promise1,它将在 1 秒内完成解析。此外,我们还定义了全局数据变量。
之后,我们使用 then() 方法来消费 promise1,并在回调函数内部,将 promise 的返回值存储在 data 中。
接下来,我们定义了 promise2,它将在 2 秒后得到解决。接下来,我们对 promise2 使用了 then() 方法,并在回调函数中使用了 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 并将其拒绝。
之后,我们使用 Promise 链式调用来消费该 Promise。我们使用了两个 then() 方法,并在所有 then() 方法之后使用了 1 个 catch() 方法。
在输出中,您可以看到,当我们拒绝 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 或执行任何其他操作。
示例
在下面的代码中,我们定义了 primise1 并在回调函数中使用了 setTimeOut() 方法。
之后,我们使用多个 then() 方法使用该 Promise。每个 then() 方法都会返回一个新的 Promise。
当您从 then() 方法仅返回值时,它会返回 Promise,该 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 作为第一个参数传递,并将匿名函数作为回调函数传递。
回调函数将 num1 值加 1 后,将结果值存储到 p 中。
接下来,我们在回调函数中调用 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>
输出

