繁体   English   中英

具有异步/等待功能的 map() 函数

[英]map() function with async/await

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

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

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

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

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

  2. 两个示例都返回一个Promise

    • async函数总是返回一个Promise

    • getResult返回一个Promise

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

const resultsPromises = myArray.map(/* map each element to a Promise */);
  1. 正如zero298 所述alnitak 演示的那样,这非常快速(同步)按顺序启动每个承诺; 但是,由于它们是并行运行的,因此每个 Promise 都会在他们认为合适的时候解决/拒绝,并且可能不会按顺序解决(履行或拒绝)。

  2. 并行运行 Promise 并使用Promise.all收集结果,或者使用for * 循环Array.prototype.reduce顺序运行它们。

或者,您可以为我维护的可链接异步 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>

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

 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来替换.then() ,也没有使用它来返回隐式 Promise,因为您的函数已经返回了 Promise。 所以它们不是必需的。

所有 Promise 的并行执行

如果你想并行运行所有 Promises,我建议简单地使用map()返回getResult的结果并生成一个 Promises 数组。 Promise 将按顺序启动,但最终将并行运行。

const resultsPromises = indicators.map(getResult);

然后你可以等待所有的承诺并使用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,那么您可以像这样使用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);

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

它不会对返回的内容做任何特别的事情。 它不会在项目上调用.then() ,它不会await任何东西。 它同步转换数据。

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

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

如果您想等待一切都完成后再继续,您需要使用Promise.all()

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

如果第一个代码片段的目的是让.map调用等待所有 Promises 在返回之前得到解决(并让这些回调按顺序运行),恐怕它不会那样工作。 .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 之前抛出错误,在这种情况下,将对getResult的调用包装在异步函数中会将抛出的错误转换为被拒绝的 Promise,这可能很有用。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM