在本篇文章中,我们将看看我们是否可以在 JavaScript 的 forEach 循环中使用异步。 有哪些选择?
JavaScript 中的异步 forEach
异步编程不适用于 Array.prototype.forEach。 它不适用于 async-await,就像它不适用于 promises 一样。
const employees = await this.getEmployees();
// BAD
await employees.forEach(async (employee) => {
await giveBonusToEmployee(employee);
});
await sendEmail('All bonuses sent');
这里有一些问题:
- 未处理迭代器函数返回的 Promise; 因此,如果其中一个出错,也不会被注意到。 如果节点 10 上没有注册 unhandledrejection 侦听器,进程就会崩溃!
- 所有奖金是同时分配的,而不是顺序分配的,因为 forEach 不会等待每个承诺一个接一个地实现。 结果,循环在所有奖金发放之前就结束了。
- 结果,在任何奖励完全发放之前,
sendEmail()
发送了电子邮件。 也许一个也不会发出; 他们可能都会抛出错误!
以下是我们可以为此实施的解决方案:
- 连续处理每个奖金。 你可以使用 for…of 构造,因为 JavaScript 支持异步等待。
for (const employee of employees) {
await giveBonusToEmployee(employee);
}
在继续下一个奖金之前,此循环将等待前一个奖励发出。 虽然它会比我们需要的更冗长,但您也可以在这种情况下使用典型的 for(…;…;…) 。
- 并行处理所有奖金。
await Promise.all(employees.map(async (employee) => {
await giveBonusToEmployee(employee);
}));
如果顺序无关紧要,则可以更快地同时分析所有员工。 这将立即开始发放所有奖金,但在发送所有奖金之前不会发送电子邮件()。
for 和 while 循环自然地与 async-await 一起运行,因为它们是在原始函数体中编写的。 但是,当您调用另一个函数时,仅当被调用函数返回一个承诺并处理该承诺时,才能使用 async-await。
因为 .reduce()
和 .map()
都会产生一个我们可能等待的承诺或一系列承诺,所以我们可以使用它们。
但是,大多数数组方法不返回承诺或允许将承诺从一个调用传递到另一个调用,因此无法异步使用它们。 因此,异步代码不能在数组内部使用,例如 array.some()
或 array.filter()
。