等待异步功能完成内部地图

Posted

技术标签:

【中文标题】等待异步功能完成内部地图【英文标题】:Waiting for async function to complete inside map 【发布时间】:2018-03-30 05:46:59 【问题描述】:

考虑下面的简单代码代码:

await Promise.all(arrayOfObjects.map(async (obj) => 
    return await someAsyncFunctionOnObj(obj);
));

我的问题是,arrayOfObjectssomeAsyncFunctionOnObj 在执行时占用了太多内存,因为循环不会等待执行完成,而是调用 someAsyncFunctionOnObj (obj),在每一个上,并等待 全部解决,不需要按顺序,这会导致 OOM 崩溃。 我试过使用递归异步函数,它确实解决了顺序问题,但仍然导致 OOM 崩溃。

我要实现的流程是同步循环,意思是

await someAsyncFunctionOnObj(obj1); // wait for it to finish
await someAsyncFunctionOnObj(obj2); // wait for it to finish
...

对如何正确实施有什么建议吗?

【问题讨论】:

首先应该是return await someAsyncFunctionOnObj(obj); 但是如果你使用Promise.all,你真的不需要async/await。或者只是await Promise.all(arrayOfObjects.map(someAsyncFunctionOnObj)); @dfsq 是的,我只是在展示基本概念。 当你想要链接 Promise.all 时,为什么还要使用它们? @Robert 这正是我的问题,链接它们的最佳方式是什么。 您可能想要来自here 的示例,只需将第一个代码示例中的 RSVP 替换为 Promise 即可。它也很好地解释了它 【参考方案1】:

解决方案

async function queueAsyncFns(fns) 
  const values = [];

  await fns.reduce((previous, current, index, array) => 
    const thenable = index === 1 ? previous() : previous;
    return thenable.then(value => 
      values.push(value);
      return index === array.length - 1 ? current().then(value => values.push(value)) : current();
    );
  );

  return values;

示例

const anArray = [1, 2, 3];
const doSomething = async (id) => await fetch(`https://jsonplaceholder.typicode.com/users/$id`).then(res => res.json());

queueAsyncFns(anArray.map((val) => () => doSomething(val))).then((val) => console.log(val));

上述功能应该可以解决您的问题。以下是它的作用的简要概述:

queueAsyncFns 接受一个函数数组,该数组返回调用异步函数的结果。通过调用每个函数并将 Promise 返回到 reducer 的下一次调用来减少该数组。在每次迭代中,异步调用的值都会累积到一个名为 values 的数组中,该数组会在所有项都被迭代后返回。

在运行示例时,可以通过查看瀑布图直观地确定函数的准确行为。您可以看到每个网络调用都是在前一个调用完成后进行的。

【讨论】:

感谢您的解释,这更好地回答了我的问题!【参考方案2】:

如果你想等待 someAsyncFunctionOnObj(obj1) 完成,然后再做同样的事情,但使用下一个对象(obj2,obj3,...),我认为你必须链接你的承诺:

var promises = arrayOfObjects.map(obj => someAsyncFunctionOnObj(obj));
await promises.reduce((m, o) => m.then(() => o), Promise.resolve());

【讨论】:

你能解释一下第二行吗? 它获取 var Promise 中的所有 Promise 并通过使用数组 reduce 一个接一个地等待它们(array reduce 使用 m 作为前一个 Promise 的内存,所以我们可以做 .then on m 并返回下一个 promise o,o 成为 m 的新值,依此类推)【参考方案3】:
(async function() 
  async function executeSequentially() 
    const tasks = [1,2]
    const total = []
    for (const fn of tasks) 
      const res = await fetch(endpoint);
      const res2 = await res.json();
      console.log(res2[0]);
      total.push(...res2);
    

    return total;
  

  const res = await executeSequentially();
  console.log(res);
)();

【讨论】:

以上是关于等待异步功能完成内部地图的主要内容,如果未能解决你的问题,请参考以下文章

在映射下一项之前,异步等待映射不等待异步函数在映射函数内部完成

异步等待在地图功能中无法正常工作

等待先前的异步功能完成[重复]

量角器 - 等待异步完成

Jest + React-testing-library-等待模拟的异步功能完成

节点在继续之前等待异步功能