为什么我在NodeJS + MongoDB中丢失变量?

时间:2013-11-19 06:54:49

标签: node.js mongoose

function getBlocks(docs){
        var jsonResults = docs;
        for(var i = 0; i < jsonResults.length; i++){
            console.log(jsonResults[i]); //Proper result
            database.Block.findOne({brand_id: docs[i]._brand[0]['_id'], timestamp: {$lte: new Date()}}, function(error, results){
                console.log(jsonResults[i]); //Undefined
                console.log(i); //Returns 2 twice (there's 2 documents)
                res.json(jsonResults);
            });
        }
    }

我对NodeJS了解所以我知道它与NodeJS的异步性质有关,但我不知道如何修复它。

编辑:我已经更新了代码。我希望在forEach完成后输出更改的数组,并且我已经附加了数据。

function getBlocks(docs){
    var jsonResults = docs;
    jsonResults.forEach(function(doc, i){
        console.log(doc);
        database.Block.findOne({brand_id: docs[i]._brand[0]['_id'], timestamp: {$lte: new Date()}}, function(error, results){

            jsonResults[i]['test'] = results;
            console.log(jsonResults[i]['test']); //Has array test. Previous line worked.

        });
    });
    res.json(jsonResults); //Outputs unchanged array.
}

1 个答案:

答案 0 :(得分:1)

是的,这是一个经典的范围界定问题。调用findOne内的回调时i已经是数组的最后一个索引。但是函数创建了一个单独的范围(与代码块不同),所以试试这个:

function getBlocks(docs){
    var jsonResults = docs;
    jsonResults.forEach(function(doc, i) {
        console.log(doc); //Proper result
        database.Block.findOne({brand_id: doc._brand[0]['_id'], timestamp: {$lte: new Date()}}, function(error, results){
            console.log(doc);
            console.log(i);
            res.json(jsonResults);
        });
    });
}

编辑另一个问题有点不同。这是一个同步的问题。您必须等到所有异步作业完成它们的操作然后继续。看看async.js。这是一个非常好的图书馆,可以帮助您以干净简单的方式实现这一目标。你可以在你的实现这个机制。尝试这样的事情:

function getBlocks(docs){
    var jsonResults = docs,
        jobs = [];

    var finalize = function() {
        // here goes the final code
        res.json(jsonResults); //Outputs unchanged array.
    };

    jsonResults.forEach(function(doc, i){
        jobs.push(i); // add current job to the pool
        console.log(doc);
        database.Block.findOne({brand_id: docs[i]._brand[0]['_id'], timestamp: {$lte: new Date()}}, function(error, results){

            jsonResults[i]['test'] = results;
            console.log(jsonResults[i]['test']); //Has array test. Previous line worked.

            // remove current job from the pool
            var idx = jobs.indexOf(i);
            if (idx != -1) {
                jobs.splice(idx, 1);
            }
            // if no more jobs then finalize
            if (typeof jobs[0] === "undefined") {
                finalize();
            }
        });
    });
}