如何在 javascript 中同步异步地图函数

How to synchronize an async map function in javascript

我有一个异步映射函数,但希望它同步执行,因为我需要在同一个循环中使用第一个语句的输出。但是即使使用 await 语句,地图也会异步运行,请您帮助理解为什么会这样。

我的用例是在 mongodb 中插入一条记录(如果不存在)并在循环中更新它(如果存在)。 数据存在于数据库中,但在循环中查找失败但在外部有效。

我的代码:

        const doSomethingAsync = () => {
            return new Promise(resolve => {
                setTimeout(() => {
                    resolve(Date.now());
                }, 1000);
            });
        };

        await Promise.all(
            modelVarients.map(async varient => {
                console.log(`varient: ${varient._id}`);
                console.log('1');
                const onlineDevice = await Device.findOne({
                    model: varient._id,
                });
                console.log('2');
                await doSomethingAsync();
                console.log('3');
                await doSomethingAsync();
                console.log(JSON.stringify(onlineDevice));
                await doSomethingAsync();
                console.log('4');
                return varient;
            })
        );

我得到的日志:

varient: 8 pro
1
varient: note
1
varient: iphone x
1
2
2
2
3
3
3
null
null
null
4
4
4

但我期望得到的是:

varient: 8 pro
1
2
3
<actual response from db for 8 pro>
4
varient: note
1
2
3
<actual response from db for note>
4
varient: iphone x
1
2
3
<actual response from db for iphone x>
4

modelVarients.map(async () => >...)将所有的元素都转化为Promises,也就是全部开始执行。然后 Promise.all() 收集它们并等待所有这些,这就是为什么你可以使用这个结构来等待 map 完成。这是并行处理。

你需要的是顺序处理,你可以用一个reduce,像这样:

await modelVarients.reduce(async (memo, varient) => {
    await memo;
    // all the other things
}, Promise.resolve())

reduce在某种意义上类似于map,它为数组中的所有元素创建一个Promise,但是有一个当前值从一个元素传递到另一个元素。在本例中,第一个是 Promise.resolve(),第二个是第一个的结果,依此类推。使用 await memo 可以等待之前的结果。

有了reduce,最后一个元素会等前一个,哪个等前一个,以此类推,所以不需要Promise.all.

我写过关于 map and reduce 如何使用异步函数的文章,它们将帮助您了解全局。

看起来你应该你的承诺,而不是运行然后与Promise.all

并行