【问题标题】:How to wait for loop to finish before saving another document?如何在保存另一个文档之前等待循环完成?
【发布时间】:2019-09-24 23:07:39
【问题描述】:

问题

我正在使用 for 循环来创建和保存多个文档,然后将它们的 id 作为对另一个文档的引用推送,我稍后会尝试保存。

但是,当我执行代码时,最终的文档保存发生在 for 循环之前,这意味着永远不会保存引用。

如何在保存另一个文档(在我的例子中是条目)之前完成 for 循环?还是有完全不同的更好的方法来完成我想要完成的事情?

我尝试过的

我尝试将代码分成两个异步等待函数,我还尝试将代码分成一个带有回调的函数,该回调在完成后调用另一个函数。我目前正在尝试更深入地理解 Promises。我也在探索是否可以设置一个标志或一个小的时间延迟。

    Template.findById(templateId, function (err, template) {

        if (err) { return console.log(err) }

        let entry = new Entry();
        entry.entryState = "Draft";
        entry.linkedTemplateId = template._id;

        for (var i = 0; i < template.components.length; i++) {

            let component = template.components[i]

            let entryComp = new entryComponent({
                componentOrder: component.componentOrder,
                componentType: component.componentType,
                templateComponentId: component._id,
                entryId: entry._id
            })

            entryComp.save(function(err){
                if (err) { return console.log(err) }
                entry.entryComponents.push(entryComp._id);
            });

        }

        entry.save(function(err){
            if (err) { return console.log(err) }
        });
    })

【问题讨论】:

  • 使用异步和等待。而不是 for 循环使用它。见下面链接lavrton.com/…
  • @sarvonks,会试试看,会在几分钟内回复你!
  • @sarvonks,如果我使用带有 Promise 的基于时间的延迟方法,它似乎适用于某些项目,有时,但不是所有项目。

标签: javascript node.js mongodb mongoose


【解决方案1】:

您可以在 javascript 中使用新的“for of”循环以及异步等待来简化您的答案:-

 Template.findById(templateId, async function (err, template) {

  try {
      if (err) { return console.log(err) }

        let entry = new Entry();
        entry.entryState = "Draft";
        entry.linkedTemplateId = template._id;

        for(const component of template.components) {
          let entryComp = new entryComponent({
                componentOrder: component.componentOrder,
                componentType: component.componentType,
                templateComponentId: component._id,
                entryId: entry._id
            })

          await entryComp.save();
          entry.entryComponents.push(entryComp._id);
        }

          await entry.save();
  } catch (error) {
    // handle error
  }
})

另一种方法是在 Promise.all 中使用普通的“for 循环”,这个会比另一个更快,因为在继续之前你不会等待一个 entryComp 保存:-

 Template.findById(templateId, async function (err, template) {

  try {
      if (err) { return console.log(err) }

        let entry = new Entry();
        entry.entryState = "Draft";
        entry.linkedTemplateId = template._id;

        const promises = [];

         for (var i = 0; i < template.components.length; i++) {

            let component = template.components[i]

            let entryComp = new entryComponent({
                componentOrder: component.componentOrder,
                componentType: component.componentType,
                templateComponentId: component._id,
                entryId: entry._id
            })

            promises.push(entryComp.save());
            entry.entryComponents.push(entryComp._id);
        }

        await Promise.all(promises);

          await entry.save();
  } catch (error) {
    // handle error
  }
})

此外,如果您正在执行相互依赖的多文档操作,则执行这些操作的正确方法是通过提供原子性的 mongodb 事务。在这里看看他们https://docs.mongodb.com/master/core/transactions/

【讨论】:

  • 感谢您的回答!我会花一些时间来探索它。这肯定是一个更复杂的答案,并且可能有一个更好的解决方案。感谢您分享执行多文档操作的正确方法。您知道与 Michael 的解决方案相比,您的解决方案是否有任何与性能相关的优势?
  • Michael 的解决方案实际上不是正确的方法,但这是他提出的一个非常有趣的解决方案。看到使用了递归,这也涉及承诺。递归将填充调用堆栈,并在列表很长的情况下产生一些性能问题。正如我所看到的,您不需要先等待一个文档被保存,然后再移动到另一个文档,为此我提到了第二个选项,这将是最快的,因为您同步调用所有保存方法,然后等待所有一次.
  • 看看这篇关于 mongodb 事务的帖子,它也会把事情弄清楚thecodebarbarian.com/…
  • 感谢您的澄清!我会考虑实施它。一旦我得到它,我可能会改变你接受的答案。
【解决方案2】:

我喜欢为此目的使用递归。尽管我非常有信心其他人在我之前就已经想到了这一点,但我确实自己发现了这个解决方案。由于我自己解决了这个问题,我不确定这是否是最好的做事方式。但我已经使用过它,它在我的应用程序中运行良好。

   Template.findById(templateId, function (err, template) {

    if (err) { return console.log(err) }

    let entry = new Entry();
    entry.entryState = "Draft";
    entry.linkedTemplateId = template._id;

    var fsm = function(i){
        if (i<template.components.length)
        {
            let component = template.components[i]

            let entryComp = new entryComponent({
            componentOrder: component.componentOrder,
            componentType: component.componentType,
            templateComponentId: component._id,
            entryId: entry._id
            })

            entryComp.save(function(err){
                if (err) { return console.log(err) }
                entry.entryComponents.push(entryComp._id);

                    //cause the fsm state change when done saving component
                     fsm(i+1)
                });


        }

        else
        {
            //the last index i=length-1 has been exhausted, and now i= length, so
            //so do not save a component, instead save the final entry 
           entry.save(function(err){
                if (err) { return console.log(err) }
                }); 

        }
    }

    //start the fsm
    fsm(0)

})

简而言之,我会使用递归来解决问题。但是,如果组件数组非常大,则可能会超出递归堆栈限制。考虑限制此应用程序中允许的项目数。

【讨论】:

  • 这太棒了!谢谢!为我省去了使用复杂的承诺和基于时间的延迟设置的麻烦,你永远无法确定!
猜你喜欢
  • 1970-01-01
  • 2017-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多