带有异步/等待的 map() 函数

IT技术 javascript asynchronous promise
2021-02-22 02:59:26

有很多关于 async/await 在 javascript map 函数中的行为的主题,但是,下面两个例子中的详细解释会很好:

  const resultsPromises = myArray.map(async number => {
    return await getResult(number);
  });
  const resultsPromises = myArray.map(number => {
    return getResult(number);
  });

编辑:这当然是一个虚构的案例,所以刚刚开始辩论,为什么,如何以及何时映射函数等待 await 关键字。解决方案如何修改这个例子,调用 Promise.all() 不是这个问题的目的。
getResult是一个异步函数

5个回答

其他答案已经很好地涵盖了您的示例行为的细节,但我想尝试更简洁地说明它。

const resultsPromises = myArray.map(async number => {
  return await getResult(number);
});
const resultsPromises = myArray.map(number => {
  return getResult(number);
});
  1. Array.prototype.map 同步循环遍历数组并将每个元素转换为其回调的返回值。

  2. 两个示例都返回 aPromise

    • async函数总是返回 a Promise

    • getResult返回一个Promise.

    • 因此,如果没有错误,您可以在伪代码中将它们视为:

const resultsPromises = myArray.map(/* map each element to a Promise */);
  1. 正如zero298 所说alnitak 所证明的那样,这非常快速(同步)按顺序启动每个Promise;然而,由于它们是并行运行的,每个 promise 都会按照他们认为合适的方式解决/拒绝,并且可能不会按顺序解决(履行或拒绝)。

  2. 并行运行 promise 并Promise.all使用for * 循环Array.prototype.reduce.

或者,您可以将第三方module用于我维护的可链接异步 JavaScript 方法来清理内容,并且——也许——使代码符合您对异步映射操作可能如何工作的直觉

const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

const getResult = async n => {
  await delay(Math.random() * 1000);
  console.log(n);
  return n;
};

(async () => {
  console.log('parallel:');
  await AsyncAF([1, 2, 3]).map(getResult).then(console.log);
  
  console.log('sequential:');
  await AsyncAF([1, 2, 3]).series.map(getResult).then(console.log)
})();
<script src="https://unpkg.com/async-af@7.0.12/index.js"></script>

return await 在异步调用中是无用且低效的,因为异步函数将以任何一种方式返回Promise
2021-04-23 02:59:26

Array.prototype.map()是一个转换数组的函数。它将一个数组映射到另一个数组。其函数签名中最重要的部分是回调。对 Array 中的每个项目调用回调,回调返回的是放入由 返回的新 Array 中的内容map

它对返回的内容没有任何特别之处。它不调用.then()项目,它不做await任何事情。它同步转换数据。

这意味着如果回调返回 a Promise(所有async函数都会这样做),所有的 promise 都将是“热的”并并行运行。

在您的示例中,如果getResult()返回 aPromise或本身是异步的,则您的实现之间没有真正的区别。 resultsPromises将由Promise可能尚未解决s填充

如果您想在继续之前等待一切完成,则需要使用Promise.all().

此外,如果您只想getResults()一次运行1 ,请使用常规for循环并await在循环内进行。

async/await当你想通过删除.then()回调来扁平化你的代码或者你想隐式返回一个 Promise时,它很有用:

const delay = n => new Promise(res => setTimeout(res, n));

async function test1() {
  await delay(200);
  // do something usefull here
  console.log('hello 1');
}

async function test2() {
  return 'hello 2'; // this returned value will be wrapped in a Promise
}

test1();
test2().then(console.log);

但是,在您的情况下,您没有使用await来替换 a .then(),也没有使用它来返回隐式 Promise,因为您的函数已经返回了一个 Promise。所以它们不是必需的。

所有 Promise 的并行执行

如果您想并行运行所有 Promise,我建议简单地返回getResultwith的结果map()并生成一组 Promise。Promise 将按顺序启动,但最终会并行运行。

const resultsPromises = indicators.map(getResult);

然后您可以等待所有Promise并使用Promise.all()以下方法获得已解决的结果

const data = [1, 2, 3];

const getResult = x => new Promise(res => {
  return setTimeout(() => {
    console.log(x);
    res(x);
  }, Math.random() * 1000)
});

Promise.all(data.map(getResult)).then(console.log);

Promise 的顺序执行

但是,如果您想按顺序运行每个 Promise 并在运行下一个 Promise 之前等待前一个 Promise 解决,那么您可以使用reduce()async/await像这样:

const data = [1, 2, 3];

const getResult = x => new Promise(res => {
  return setTimeout(() => {
    console.log(x);
    res(x);
  }, Math.random() * 1000)
});

data.reduce(async (previous, x) => {
  const result = await previous;
  return [...result, await getResult(x)];
}, Promise.resolve([])).then(console.log);

如果第一个代码片段的目的.map是在返回之前等待所有 Promise 得到解决(并让这些回调按顺序运行),那么我担心它不会像那样工作。.map函数不知道如何使用async函数来做到这一点

这可以用以下代码演示:

const array = [ 1, 2, 3, 4, 5 ];
      
function getResult(n)
{
    console.log('starting ' + n);

    return new Promise(resolve => {
        setTimeout(() => {
            console.log('finished ' + n);
            resolve(n);
        }, 1000 * (Math.random(5) + 1));
    });
}

let promises = array.map(async (n) => {
    return await getResult(n);
});

console.log('map finished');

Promise.all(promises).then(console.log);

您将看到.map调用在任何异步操作完成之前立即完成。

如果getResult总是返回一个Promise并且从不抛出错误,那么两者的行为都是一样的。

一些Promise返回函数可能会在返回Promise之前抛出错误,在这种情况下,将调用包装getResult在异步函数中会将抛出的错误转换为被拒绝的Promise,这可能很有用。

正如许多评论中所述,您永远不需要return await- 它相当于.then(result=>result)在Promise链的末尾添加- 它(大部分)无害但不必要。只需使用return.