在 catch 块中等待在 forEach 中失败

Par*_*ord 2 javascript foreach try-catch async-await

我有一个类似数组的结构,它公开异步方法。异步方法调用包含 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));
}
Run Code Online (Sandbox Code Playgroud)

jib*_*jib 10

我想了解为什么forEach不能很好地使用async/ await

当我们认为这async只是返回承诺的函数的语法糖时,这会更容易。

项目。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));
Run Code Online (Sandbox Code Playgroud)

...但tryToProcess对每个项目的所有调用将彼此并行执行。

重要的是,map并行运行它们。Promise.all只是等待它们完成的一种手段。

按照惯例...

我总是在函数中使用for of而不是:forEachasync

for (item of items) {
  await tryToProcess(item);
}
Run Code Online (Sandbox Code Playgroud)

...即使await在循环中没有,以防万一我以后添加一个,以避免这个脚枪。


Cer*_*nce 3

没有办法像那样使用forEachwith -不能串行运行异步迭代,只能并行运行(即使这样,with会更好)。相反,如果您想使用数组方法,请使用上一次迭代的 Promise 的分辨率:awaitforEachmapPromise.allreduceawait

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));
}
Run Code Online (Sandbox Code Playgroud)

另请注意,如果函数中的 onlyawait正好在函数返回之前,那么您也可以只返回 Promise 本身,而不是让函数为async