nodejs和mongoskin,保存所有项目后的回调

时间:2015-10-05 19:12:17

标签: node.js mongodb mongodb-query mongoskin

我有以下代码,我遍历一个集合并执行另一个db查询并在其回调中构造一个对象。最后,我将该对象保存到另一个集合中。

我想在保存所有项目后调用另一个函数,但无法弄清楚如何。我尝试使用异步库,特别是异步,而当item不为null时,这只会让我陷入无限循环。

有没有办法确定所有项目何时保存?

谢谢!

var cursor = db.collection('user_apps').find({}, {timeout:false});
cursor.each(function (err, item) {
    if (err) {
        throw err;
    }
    if (item) {
        var appList = item.appList;
        var uuid= item.uuid;
        db.collection('app_categories').find({schema_name:{$in: appList}}).toArray(function (err, result) {
            if (err) throw err;
            var catCount = _.countBy(result, function (obj) {
                return obj.category;
            })
            catObj['_id'] = uuid;
            catObj['total_app_num'] = result.length;
            catObj['app_breakdown'] = catCount;
            db.collection('audiences').insert(catObj, function (err) {
                if (err) console.log(err);
            });
        }); 
    }
    else {
        // do something here after all items have been saved
    }
});

1 个答案:

答案 0 :(得分:2)

这里的关键是在执行“循环”操作时使用一些会尊重回调信号的东西。这里实现的.each()不会那样做,所以你需要一个“异步”循环控件来表示每个循环已经迭代完成,并且它在回调中有自己的回调。

如果你的底层MongoDB驱动程序至少是版本2,那么有一个.forEach(),它有一个回调,当循环完成时会调用它。这比.each()更好,但它无法解决内部“异步”.insert()操作何时完成的问题。

因此,更好的方法是使用.find()返回的stream interface,其中允许更多流量控制。有一个.stream()方法可以向后兼容,但现代驱动程序默认只返回接口:

var stream = db.collection('user_apps').find({});

stream.on("err",function(err){
    throw(err);
});

stream.on("data",function(item) {
    stream.pause();                 // pause processing of stream
    var appList = item.appList;
    var uuid= item.uuid;
    db.collection('app_categories').find({schema_name:{ "$in": appList}}).toArray(function (err, result) {
        if (err) throw err;
        var catCount = _.countBy(result, function (obj) {
            return obj.category;
        })

        var catObj = {};        // always re-init
        catObj['_id'] = uuid;
        catObj['total_app_num'] = result.length;
        catObj['app_breakdown'] = catCount;
        db.collection('audiences').insert(catObj, function (err) {
            if (err) console.log(err);
            stream.resume();        // resume stream processing
        });
    }); 
});

stream.on("end",function(){
    // stream complete and processing done
});

流上的.pause()方法会停止发出更多事件,以便每次处理一个对象结果。当调用来自.insert()的回调时,将调用.resume()方法,表示该项目的处理已完成,并且可以进行新的调用以处理下一个项目。

当流完成后,一切都完成,所以调用“end”事件挂钩继续你的代码。

这样,每个循环都用一个结尾表示移动到下一个迭代,并且有一个定义的“结束”事件用于完整的处理结束。由于控件在.insert()回调的“内部”,因此这些操作也可以完成。

作为旁注,您可以考虑在源集合中包含您的“类别”信息,因为如果所有必需数据都在一个集合中,您可能会使用.aggregate()更有效地返回结果。< / p>