我有一个类似于数组的结构,它暴露了异步方法。异步方法调用包含try-catch块,这些块在捕获的错误的情况下会显示更多的异步方法。我想了解为什么forEach
与async
/ await
不能很好地协作。
let items = ['foo', 'bar', 'baz'];
// Desirable behavior
processForLoop(items);
/* Processing foo
* Resolved foo after 3 seconds.
* Processing bar
* Resolved bar after 3 seconds.
* Processing baz
* Resolved baz after 3 seconds.
*/
// Undesirable behavior
processForEach(items);
/* Processing foo
* Processing bar
* Processing baz
* Resolved foo after 3 seconds.
* Resolved bar after 3 seconds.
* Resolved baz after 3 seconds.
*/
async function processForLoop(items) {
for(let i = 0; i < items.length; i++) {
await tryToProcess(items[i]);
}
}
async function processForEach(items) {
items.forEach(await tryToProcess);
}
async function tryToProcess(item) {
try {
await process(item);
} catch(error) {
await resolveAfter3Seconds(item);
}
}
// Asynchronous method
// Automatic failure for the sake of argument
function process(item) {
console.log(`Processing ${item}`);
return new Promise((resolve, reject) =>
setTimeout(() => reject(Error('process error message')), 1)
);
}
// Asynchrounous method
function resolveAfter3Seconds(x) {
return new Promise(resolve => setTimeout(() => {
console.log(`Resolved ${x} after 3 seconds.`);
resolve(x);
}, 3000));
}
答案 0 :(得分:2)
无法像forEach
一样使用await
- forEach
无法在串行中运行异步迭代,只能并行运行(即使这样,map
也会{ {1}}会更好)。相反,如果您想使用数组方法,请使用Promise.all
和reduce
解析前一次迭代的Promise:
await
另请注意,如果函数中的唯一let items = ['foo', 'bar', 'baz'];
processForEach(items);
async function processForLoop(items) {
for (let i = 0; i < items.length; i++) {
await tryToProcess(items[i]);
}
}
async function processForEach(items) {
await items.reduce(async(lastPromise, item) => {
await lastPromise;
await tryToProcess(item);
}, Promise.resolve());
}
async function tryToProcess(item) {
try {
await process(item);
} catch (error) {
await resolveAfter3Seconds(item);
}
}
// Asynchronous method
// Automatic failure for the sake of argument
function process(item) {
console.log(`Processing ${item}`);
return new Promise((resolve, reject) =>
setTimeout(() => reject(Error('process error message')), 1)
);
}
// Asynchrounous method
function resolveAfter3Seconds(x) {
return new Promise(resolve => setTimeout(() => {
console.log(`Resolved ${x} after 3 seconds.`);
resolve(x);
}, 3000));
}
就在函数返回之前,您也可以只返回Promise本身,而不是让函数为await
。
答案 1 :(得分:1)
我想了解为什么
forEach
与async
/await
不能很好地配合。
当我们认为async
只是返回承诺的函数的语法糖时,这会更容易。
items。forEach(f)期望函数f
作为参数,它在返回之前在每个项目上执行一次。它忽略了f
的返回值。
items.forEach(await tryToProcess)
无效等同于Promise.resolve(tryToProcess).then(ttp => items.forEach(ttp))
与items.forEach(tryToProcess)
在功能上没有区别。
现在tryToProcess
会返回一个承诺,但forEach
会忽略返回值,正如我们所提到的那样,所以它忽略了这个承诺。这是坏消息,并且可能导致未处理的拒绝错误,因为所有承诺链都应该使用catch
返回或终止以正确处理错误。
这个错误等同于遗忘await
。不幸的是,没有array.forEachAwait()
。
项。map(f)稍好一些,因为它会从f
的返回值中创建一个数组,在tryToProcess
的情况下会给我们一个数组承诺。例如。我们可以这样做:
await Promise.all(items.map(tryToProcess));
...但每个项目的所有tryToProcess
次调用都会相互执行 。
重要的是,map
并行运行它们。 Promise.all
只是等待完成的一种方式。
我总是在for of
个函数中使用forEach
代替async
:
for (item of items) {
await tryToProcess(item);
}
...即使循环中没有await
,以防我稍后添加一个,以避免这种脚枪。