【问题标题】:Synchronous for loop in node js节点js中的同步for循环
【发布时间】:2015-07-18 08:00:24
【问题描述】:

假设我有以下 for 循环

for(var i = 0; i < array.length; i++){
   Model.findOne({ _id = array[i].id}, function(err, found){
    //Some stuff
   });
}

如何使这段代码工作?每次运行它我都会得到array[i] = undefined因为mongo-db 查询是异步的,并且在第一个查询完成时循环已经迭代了5 次。我该如何解决这个问题并等待查询完成后再进行下一次迭代?

【问题讨论】:

  • 通过分配给另一个变量使其同步。 var result = Model.findOne({ _id = array[i].id}, function(err, found){ //一些东西 });
  • 如果您使用Array.prototype.forEach,则item 在回调中与在其外部相同。或者,如果您使用 --harmony 标志,并将 var i 替换为 let i,它也可以工作。但是对于这两种解决方案,我怀疑您还会遇到更多问题。

标签: node.js mongodb asynchronous promise synchronous


【解决方案1】:

这不是专门回答您的问题,而是解决您的问题。

我会使用 $in 查询并一次完成所有过滤。与 1 次相比,对 db 的 20 次调用相当慢:

// grab your ids
var arrayIds = myArray.map(function(item) {
    return item._id;
});

// find all of them
Model.find({_id: {$in: arrayIds}}, function(error, foundItems) {

    if (error) {
        // error handle
    }

    // set up a map of the found ids
    var foundItemsMap = {};
    foundItems.forEach(function(item) {
        foundItemsMap[item._id] = true;
    });

    // pull out your items that haven't been created yet
    var newItems = [];
    for (var i = 0; i < myArray.length; i++) {

        var arrayItem = myArray[i];

        if ( foundItemsMap[arrayItem._id] ) {
            // this array item exists in the map of foundIds
            // so the item already exists in the database
        }
        else {
            // it doesn't exist, push it into the new array
            newItems.push(arrayItem);
        }
    }

    // now you have `newItems`, an array of objects that aren't in the database
});

【讨论】:

  • 您可能需要在其中为 _id 输入一些 .toString()s,但我不能 100% 确定
  • 这对我来说非常有效,我不需要将 .toString() 添加到任何 ID。
【解决方案2】:

完成您想要的事情的最简单方法之一是使用 Promise。您可以使用库 q 来执行此操作:

var Q = require('q');

function fetchOne(id) {
    var deferred = Q.defer();
    Model.findOne({ _id = id}, function(err, found){
        if(err) deferred.reject(err);
        else    deferred.resolve(found);
    });
    return deferred.promise;
}
function fetch(ids, action) {
    if(ids.length === 0) return;
    var id = ids.pop();
    fetchOne(id).then(function(model) {
        action(model);
        fetch(ids, action);
    });
}

fetch([1,2,3,4,5], function(model) { /* do something */ });

这不是最漂亮的实现,但我相信你明白了 :)

【讨论】:

  • 我不确定,为什么你会考虑同步多个findOne。也许您可以详细说明以获得更好的答案?
  • 想一想,也许你可以不做任何承诺。
  • 我有一个必须添加到数据库的项目列表,但首先我必须检查它们是否已经存在。我正在制作多个 findOne 类,以检查它们是否存在。
  • 感谢您的回答,埃米尔! Promise 的替代方案是什么?
  • 好的,那么我将推荐另外两个选项来研究。一个是简单的改进,您首先使用find$in 找到所有ID。这样您就可以从数据库中获取所有文档,然后插入缺少的文档。更好的选择是使用 upsert,它是插入和更新的组合,如果缺少文档,它将插入文档,否则将更新它。最后一个解决方案是原子的,这将避免竞争条件。
【解决方案3】:

不确定这是否正确,可能有点贵,但我就是这样做的。 我认为诀窍是提取所有数据,然后查找 id 匹配项。

Model.find(function(err, data) {
    if (err) //handle it

    for (var i=0; i<array.length; i++) {
        for (var j=0; ij<data.length; j++) {
            if(data[j].id == array[i].id) {
               // do something
            }
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-01
    • 2019-08-18
    • 2017-11-22
    相关资源
    最近更新 更多