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

时间:2019-03-18 15:54:24

标签: javascript asynchronous promise

关于javascript映射函数中async / await的行为,已经发布了很多主题,但是下面的两个示例中的详细说明还是不错的:

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

编辑过:这当然是一个虚构的案例,因此才开始辩论,为什么,如何以及何时将map函数等待await关键字。解决方案如何修改此示例,调用Promise.all()并非此问题的目的。
getResult是一个异步函数

5 个答案:

答案 0 :(得分:2)

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

对于返回的内容,它没有做任何特殊的事情。它不会对项目调用.then(),也不会await。它可以同步转换数据。

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

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

如果要继续进行之前要等待所有事情完成,则需要使用Promise.all()

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

答案 1 :(得分:1)

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

并行执行所有承诺

如果要并行运行所有Promises,我建议仅将getResultmap()一起返回结果并生成Promises数组。 Promises将按顺序启动,但最终将并行运行。

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之前等待其解决,则可以像这样使用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);

答案 2 :(得分:1)

如果getResult总是返回一个承诺并且从不抛出错误,那么两者的行为将相同。

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

正如许多评论中所述,您永远不需要return await-相当于在诺言链的末尾添加.then(result=>result)-(主要)无害,但并不重要。只需使用return

答案 3 :(得分:1)

如果第一个代码段的目的是进行一个.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调用在任何异步操作完成之前立即结束。

答案 4 :(得分:1)

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

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 statedalnitak demonstrated一样,这很快(同步)按顺序开始了每个承诺;但是,由于它们是并行运行的,每个诺言都会在他们认为合适的情况下解决/拒绝,并且可能不会按顺序解决(履行或拒绝)。

  2. 要么并行运行promise,然后使用Promise.all收集结果,要么使用for * loopArray.prototype.reduce顺序运行它们。

或者,您可以为我维护的chainable asynchronous JavaScript methods使用第三方模块来清理内容,也许-使代码符合您对async map操作的工作原理的直觉:< / p>

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>