关于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
是一个异步函数
答案 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,我建议仅将getResult
与map()
一起返回结果并生成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);
});
Array.prototype.map
同步遍历数组,并将每个元素转换为其回调的返回值。
两个示例返回一个Promise
。
async
函数始终返回Promise
。
getResult
返回一个Promise
。
因此,如果没有错误,您可以在伪代码中将它们都视为:
const resultsPromises = myArray.map(/* map each element to a Promise */);
与zero298 stated和alnitak demonstrated一样,这很快(同步)按顺序开始了每个承诺;但是,由于它们是并行运行的,每个诺言都会在他们认为合适的情况下解决/拒绝,并且可能不会按顺序解决(履行或拒绝)。
要么并行运行promise,然后使用Promise.all
收集结果,要么使用for * loop或Array.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>