与 Promise.all() 中的解析相比,为啥在 while 循环中单独解析 Promise 数组时解析更慢? [复制]

Posted

技术标签:

【中文标题】与 Promise.all() 中的解析相比,为啥在 while 循环中单独解析 Promise 数组时解析更慢? [复制]【英文标题】:Why is resolution of array of promises slower when resolved individually in a while loop, compared to resolution in Promise.all()? [duplicate]与 Promise.all() 中的解析相比,为什么在 while 循环中单独解析 Promise 数组时解析更慢? [复制] 【发布时间】:2021-03-21 21:05:35 【问题描述】:

在我的 mongoDB 数据库中,我有一个 Posts 模型,其中包含对 Comments 模型的引用数组。

对于我的 Express 应用程序中的一个 get 请求,我必须使用数据库中所有帖子的数组来响应,每个帖子都包含它拥有的所有 cmets 的数组

我选择这样做的方法是,使用 find 获取所有帖子,它返回所有帖子的数组,根据我的模型,每个帖子都包含对其拥有的 cmets 的引用数组。

为了解决这些引用,我选择了 mongoose 提供的填充方法,它根据它包含的引用从数据库中返回实际对象,这对于帖子来说非常简单

await post.populate("comments").execPopulate();

这里的帖子是单个帖子实例,在此操作之后,帖子实例包含解析为评论对象的所有评论引用

但要为每个帖子单独执行此操作,我尝试了两种方法,其中allPosts 是我在查找查询后获得的所有帖子的数组

    使用上面的 execPopulate 操作,创建一个包含所有 promise 的数组,不管它们是否被解析,然后在全部解析后使用 Promise.all() 执行进一步的操作
let promiseArray = allPosts.map( (post) => 
    post.populate("comments").execPopulate();
);


Promise.all(promiseArray).then((posts) => 

    // this map is another extra irrelevant operation I performed to 
    // format the response array in a particular way, where
    // I had to remove extra fields in both the posts array
    // as well as the populated comments array

    
    let responseArray = posts.map((post) => 
        return 
            comments: filterComments(post.comments),
            _id: post._id,
            title: post.title,
            commentcount: countComments(post.comments),
        ;
    );

    return res.send(responseArray);
);

    我尝试的另一种方法是设置一个 while 循环,在其中我等待每个帖子在每次迭代中单独解析,然后将解析和格式化的帖子对象推送到一个数组(响应数组)中,完成后发送响应数组
let responseArray = [];
let length = allPosts.length;
let counter = allPosts.length;

while(counter) 

    // get the first post because counter is reverse
    let post = allPosts[length - counter]

    // the population operation where every post array
    // is populated with its comments
    await post.populate('comments').execPopulate();

    // push the filtered object to another array
    responseArray.push(
        comments        : filterComments(post.comments),
        _id             : post._id,
        title           : post.title,
        commentcount    : countComments(post.comments),
    )

    counter --;


return res.send(responseArray);

应用程序在使用 while 循环解析单个 promise 时花费了三次时间来响应大约 150 个左右的同一组数据,并且当我将所有 promise 存储在数组中然后在 Promise.all() 中解析它们时速度更快, 我对 JS 事件循环不是很详细,所以想知道是否有什么原因导致这个

TL;DR:

我有一个要对数组中的每个元素执行的异步操作的列表,我试过了:

    遍历数组将所有未解析的promise存储在数组中,然后在promise数组上使用Promise.all()后做进一步的操作

    遍历数组使用while循环,对每个元素执行异步操作在每次迭代中

while 循环花费几乎三倍的时间,Promise.all() 上的解析需要

【问题讨论】:

allPosts.map( (post) => post.populate("comments").execPopulate(); ); 不会产生一个 promise 数组,而是一个 undefined 数组。所以Promise.all([undefined, undefined, /* .... */, undefined])基本会马上解决。 你应该在你的 map 方法中删除括号 无论如何,在循环中,您要等待每个操作完成,然后再进行下一个操作。哪个会先完成 - 一次设置三个计时器,每个计时器 1 分钟并等待所有人完成,或者将计时器设置为一分钟,一旦它再次完成,然后第三次? 不,它们并没有导致 undefined,而是 console.log 上的数组给出了一个 Promise() 数组 await 在一个循环中按顺序运行所有填充调用。当然那更慢! 【参考方案1】:

我不是专业的 javascript 开发人员,但据我所知。

事件循环不会在单个线程上运行,因此在使用 Promise.all() 时,promise 会同时执行。 通常,使用 Promise.all() 会并行运行“异步”请求。 并行执行可以节省时间,这就是 Promise.all() 对于每个 Promise 都比 await 更快的原因。

注意: Promise 将在创建后执行。在您的代码中,您已经在 map 方法中创建了 Promise。

Promise.all 只是等待每个承诺得到履行。

相反,async/await 创建一个 promise 等待它然后创建另一个。

但是,您可以直接填充所有帖子的 cmets。使用 下面的代码,比这两种解决方案都快

const PostsWithComments = await PostsModel.find().populate("comments").exec()

【讨论】:

是的,我试过了,对于一个帖子来说很好,但是我需要为一系列帖子做这个,所以唯一的方法是在某个循环中,我用while 循环来做它 Promise.all 不会引发并行性或多线程。 “承诺”也不是“任务”,它是一个待处理的值。该任务甚至可能根本不会在 JS 引擎中执行。例如,访问数据库,意味着您发出请求并等待从中获取结果,无需处理。 @Mayur populate 也适用于一系列帖子。 sn-p 也应该适用于多个帖子。您不需要为此进行后期操作。猫鼬会为你做的。 @RamanShekhawat 哦,从来没有想过要立即为整个阵列这样做,我想我会尝试并检查一下,不管这个问题如何,我仍然对为什么承诺速度较慢很感兴趣在while循环中解决 这不是准确的信息。

以上是关于与 Promise.all() 中的解析相比,为啥在 while 循环中单独解析 Promise 数组时解析更慢? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript 中的 Promise.all:如何获得所有承诺的解析值?

为啥我的 apolloFetch 调用在从 promise.all 中调用时返回一个空查询?

为啥即使我 .catch() Promise.all() 也会抛出异常?

Promise.all:解析值的顺序

React Native 为啥我的代码在完成任务之前执行? Promise.all().then() 异步问题

JavaScript Promise.all - 如何检查解析状态?