如何在 javascript 中同步异步映射函数

Posted

技术标签:

【中文标题】如何在 javascript 中同步异步映射函数【英文标题】:How to synchronize an async map function in javascript 【发布时间】:2020-12-24 12:15:08 【问题描述】:

我有一个异步映射函数,但希望它同步执行,因为我需要在同一个循环中使用第一条语句的输出。但是即使使用 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

【问题讨论】:

这能回答你的问题吗? Is Node.js native Promise.all processing in parallel or sequentially? 使用 await modeVariants.reduce( async (...) => ) 而不是 map @olkvin 谢谢,我会尝试在我的代码中实现 @EdwardRomero 感谢您向我指出一个类似的问题,是的,它帮助我了解了 reduce 以及如何链接承诺,下面来自 sashee 使用 async/await 的答案也非常有帮助 【参考方案1】:

看起来你应该链接你的承诺,而不是与Promise.all并行运行

【讨论】:

【参考方案2】:

modelVarients.map(async () =&gt; &gt;...) 将所有元素转换为 Promise,这意味着它们都开始执行。然后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 和 reduce 如何使用异步函数的文章,它们将帮助您了解全局。

【讨论】:

使用reduceasync function 作为回调是一个坏主意,它效率低下而且很容易完全出错。 @Bergi 你能详细说明为什么你认为这是一个坏主意,当我研究 reduce 时,甚至官方文档也有一个类似的例子 developer.mozilla.org/en-US/docs/Web/javascript/Reference/… @Sai 使用的是then 而不是await,并且在数组中链接(少数)不同的函数,而不是在数组中的(许多)项上链接相同的函数。 一般来说,只需使用for … of 循环和await - 更简单、更短、更快。 es-lint 不鼓励 for ... of when 此错误:“迭代器/生成器需要 regenerator-runtime,这对于本指南来说太重了,无法允许它们。另外,应避免循环以支持数组迭代。” eslint.org/docs/rules/no-restricted-syntax 有什么想法吗?

以上是关于如何在 javascript 中同步异步映射函数的主要内容,如果未能解决你的问题,请参考以下文章

Javascript:在函数中混合异步与同步(缓存和 Ajax 请求)

如何在javascript中同步调用一组函数

深入理解javascript编程中的同步和异步

Javascript 默认是同步(阻塞)还是异步(非阻塞)

JavaScript 异步操作之回调函数

第十次总结 线程的异步和同步