📅  最后修改于: 2023-12-03 15:09:52.111000             🧑  作者: Mango
在 Javascript 中,循环和异步通常是两个互斥的概念。循环通常是同步的,而异步则是非阻塞的。然而,有时候我们需要在循环中执行异步操作。本文将介绍如何在 Javascript 中实现循环异步。
假设我们有一个数组,需要对其中的每个元素执行异步操作,并以此为基础更新该元素的值。我们可以使用 for 循环来遍历数组并执行异步操作,但这样做会出现问题。
const arr = [1,2,3,4];
for (let i = 0; i < arr.length; i++) {
setTimeout(() => {
arr[i] = arr[i] * 2;
console.log(arr[i]);
}, 1000);
}
上述代码中,我们使用 setTimeout 模拟异步操作,并将每个元素乘以 2。然而,最终结果并不是我们所期望的。
输出结果:
undefined
undefined
undefined
undefined
这是因为 for 循环在遍历时不会等待异步操作的完成,而是直接执行下一次循环。因此,当异步操作完成时,for 循环已经执行完毕,i 的值也已经超出数组索引范围,导致我们无法正确地更新数组元素的值。
为了解决这个问题,我们需要使用控制流管理异步操作的完成,而不是依赖循环。
我们可以使用递归函数来模拟循环的行为,并在每次异步操作完成后递归调用该函数。以 Promise 的形式实现:
const arr = [1,2,3,4];
let i = 0;
function updateArray(arr, i) {
return new Promise(resolve => {
setTimeout(() => {
arr[i] = arr[i] * 2;
console.log(arr[i]);
resolve();
}, 1000);
}).then(() => {
if (++i < arr.length) {
return updateArray(arr, i);
}
});
}
updateArray(arr, i);
上述代码中,我们用 Promise 包装了异步操作,并使用递归调用 updateArray 函数来模拟循环的行为。当异步操作完成后,resolve Promise 并递归调用 updateArray 函数。直到 i 的值达到数组的长度为止。
我们也可以使用 async/await 语法糖来简化代码。
const arr = [1, 2, 3, 4];
async function updateArray(arr) {
for (let i = 0; i < arr.length; i++) {
await new Promise(resolve => {
setTimeout(() => {
arr[i] = arr[i] * 2;
console.log(arr[i]);
resolve();
}, 1000);
});
}
}
updateArray(arr);
使用 async/await 语法糖可以更简洁地实现递归代码的功能。我们使用 for 循环遍历数组,并在每次异步操作完成后使用 await 关键字等待异步操作完成。由于 await 关键字的存在,我们无需担心异步操作完成的时间问题。
在 Javascript 中实现循环异步操作是一项常见的任务,但需要小心处理异步操作的顺序以及避免循环时的阻塞问题。使用递归或者 async/await 语法糖可以解决这些问题,并带来更加清晰和简洁的代码实现。