【问题标题】:How to write an asynchronous for-each loop in Express.js and mongoose?如何在 Express.js 和 mongoose 中编写异步 for-each 循环?
【发布时间】:2014-01-05 23:32:14
【问题描述】:

我有一个从 MongoDB 返回项目数组的函数:

var getBooks = function(callback){
    Comment.distinct("doc", function(err, docs){
            callback(docs);
        }
    });
};

现在,对于文档中返回的每个项目,我想执行另一个 mongoose 查询,收集特定字段的计数,将它们全部收集到 counts 对象中,最后将其传递给 res.render

getBooks(function(docs){
    var counts = {};
    docs.forEach(function(entry){
        getAllCount(entry, ...){};
    });      
});

如果我将res.render 放在forEach 循环之后,它将在计数查询完成之前执行。但是,如果我将它包含在循环中,它将为每个条目执行。这样做的正确方法是什么?

【问题讨论】:

    标签: javascript node.js mongodb mongoose


    【解决方案1】:

    我建议使用流行的 NodeJS 包,async。这比做工作/计数要容易得多,another 答案需要最终的错误处理。

    特别是,我建议考虑each (reference):

    getBooks(function(docs){
        var counts = {};
        async.each(docs, function(doc, callback){
            getAllCount(entry, ...);         
            // call the `callback` with a error if one occured, or 
            // empty params if everything was OK. 
            // store the value for each doc in counts
        }, function(err) {
           // all are complete (or an error occurred)
           // you can access counts here
           res.render(...);
        });      
    });
    

    或者你可以使用map (reference):

    getBooks(function(docs){
        async.map(docs, function(doc, transformed){
            getAllCount(entry, ...);         
            // call transformed(null, theCount);
            // for each document (or transformed(err); if there was an error); 
        }, function(err, results) {
           // all are complete (or an error occurred)
           // you can access results here, which contains the count value
           // returned by calling: transformed(null, ###) in the map function
           res.render(...);
        });      
    });
    

    如果并发请求过多,可以使用mapLimiteachLimit函数来限制并发异步mongoose请求的数量。

    【讨论】:

    • 反对按钮用于无用的答案,而不是不使用您首选方法的答案。
    【解决方案2】:

    forEach 可能不是你最好的选择,除非你希望所有对getAllCount 的调用都并行发生(也许你会这样做,我不知道——或者就此而言,Node 仍然是单一的——默认线程,不是吗?)。相反,只保留一个索引并重复调用docs 中的每个条目,直到你完成似乎更好。例如:

    getBooks(function(docs){
        var counts = {},
            index = 0,
            entry;
    
        loop();
    
        function loop() {
            if (index < docs.length) {
                entry = docs[index++];
                getAllCount(entry, gotCount);
            }
            else {
                // Done, call `res.render` with the result
            }
        }
        function gotCount(count) {
            // ...store the count, it relates to `entry`...
    
            // And loop
            loop();
        }
    });
    

    如果您希望调用并行发生(或者如果您可以依赖此在单线程中工作),只需记住有多少未完成,以便您知道何时完成:

    // Assumes `docs` is not sparse
    getBooks(function(docs){
        var counts = {},
            received = 0,
            outstanding;
    
        outstanding = docs.length;
        docs.forEach(function(entry){
            getAllCount(entry, function(count) {
                // ...store the count, note that it *doesn't* relate to `entry` as we
                // have overlapping calls...
    
                // Done?
                --outstanding;
                if (outstanding === 0) {
                    // Yup, call `res.render` with the result
                }
            });
        });      
    });
    

    【讨论】:

    • 很好的解释... :)
    • 很好,我喜欢跟踪是否已处理所有内容的第二种解决方案。在这种情况下使用async.each 方法会更好吗,以便并行处理内容?
    • @mart1n:这些天我对 Node 的了解还不够(有一段时间没有使用它,事情已经发生了变化)来回答这个问题。我最后一次听说,Node 使用单线程和调度循环,就像浏览器对主 JavaScript 线程所做的那样。 (例如,当不使用网络工作者时。)因此,在异步时,Node 本身是单线程的。如果这种情况发生了变化,是的,您将需要使用所涉及的任何语义来获得适当的并行性。
    【解决方案3】:

    事实上,第一项上的getAllCount必须回调第二项上的getAllCount,...

    两种方式:您可以使用框架,例如异步:https://github.com/caolan/async

    或者自己创建回调链。第一次写很有趣。

    编辑 我们的目标是有一个像我们写的那样进行的机制。

    getAllCountFor(1, function(err1, result1) {
        getAllCountFor(2, function(err2, result2) {
            ...
                getAllCountFor(N, function(errN, resultN) {
                    res.sender tout ca tout ca
                });
        });
    });
    

    这就是您将使用序列格式使用异步构造的内容。

    【讨论】:

    • 但是即使我要写长的回调链,我在哪里包含res.render?如果它在回调链的末尾,它将为循环中的每个条目执行,对吗?
    猜你喜欢
    • 1970-01-01
    • 2013-08-25
    • 2012-04-07
    • 1970-01-01
    • 2021-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-12
    相关资源
    最近更新 更多