如何在node.js和MongoDb中的javascript中混合同步和异步代码

时间:2015-02-20 01:22:51

标签: javascript node.js mongodb

我有这段代码:

var re = new RegExp("<a href=\"(news[^?|\"]+).*?>([^<]+)</a>", "g");
var match;
while (match = re.exec(body)){
    var href = match[1];
    var title = match[2];
    console.log(href);

    db.news.findOne({ title: title }, function(err, result){
        if (err) {
            console.log(err);
        } else {
            console.log(href);
            // more codes here
        }
    });
}

以下是示例输出:

news/2015/02/20/347332.html
news/2015/02/19/347307.html
news/2015/02/19/347176.html
news/2015/02/19/347176.html
news/2015/02/19/347176.html
news/2015/02/19/347176.html

所以,我有三组数据要传递给findOne函数。但是,只有最后一个传递了三次。如何解决?

基于jfriend00和Neta Meta的UPDATE,这两种方法可以让它发挥作用:

var re = new RegExp("<a href=\"(news[^?|\"]+).*?>([^<]+)</a>", "g");
var cnt = 0;
function next(){
    var match = re.exec(body);
    if (match) {
        var href = match[1];
        var title = match[2];
        db.news.findOne({ title: title }, function(err, result){
            if (err) {
                console.log(err);
            } else {
                console.log(href);
                // more codes here
            }
        });
    }
}
next();

或者

var asyncFunction = function(db, href, title){
    db.news.findOne({ title: title }, function(err, result){
        if (err) {
            console.log(err);
        } else {
            console.log(href);
            // more codes here
        }
    });
}

var re = new RegExp("<a href=\"(news[^?|\"]+).*?>([^<]+)</a>", "g");
var match;
var cnt = 0;
while (match = re.exec(body)) {
    asyncFunction(db, match[1], match[2]);
}

2 个答案:

答案 0 :(得分:2)

您没有得到预期输出的原因是因为您正在共享所有数据库调用的hreftitle变量。因此,对于每个异步数据库操作,不会单独跟踪它们。

如果您可以立即执行所有异步函数并且可以按任何顺序处理数据,那么您只需要创建一个闭包,以便为每次循环调用分别捕获局部变量:

var re = new RegExp("<a href=\"(news[^?|\"]+).*?>([^<]+)</a>", "g");
var match, cntr = 0;
while (match = re.exec(body)){
    (function(href, title, index) {
        console.log(href);
        db.news.findOne({ title: title }, function(err, result){
            if (err) {
                console.log(err);
            } else {
                console.log(href);
                // more codes here
            }
        });
    })(match[1], match[2], cntr++);
}

如果你想连续发出请求(一次只发一个),那么你就不能真正使用while循环来控制事物,因为它会立即启动它们。我倾向于使用这种类型的设计模式,使用next()本地函数而不是while循环进行串行操作:

function someFunction() {

    var re = new RegExp("<a href=\"(news[^?|\"]+).*?>([^<]+)</a>", "g");

    function next() {
            var match = re.exec(body);
            if (match) {
                var href = match[1];
                var title = match[2];

                db.news.findOne({ title: title }, function(err, result){
                    if (err) {
                        console.log(err);
                    } else {
                        console.log(href);
                        // more codes here

                        // launch the next iteration
                        next();
                    }
                });
            }
    }

    // run the first iteration
    next();
}

使用promises,您可以promisify() db.news.findOne()函数,以便它返回一个promise,将所有匹配收集到一个数组中,然后使用.reduce()对所有数据库调用进行排序提供排序的.then()方法。

答案 1 :(得分:1)

你只得到最后一个href的原因是因为迭代并调用了一个asyc操作的fineOne。虽然不会等到findOne完成它只是在findOne完成时继续运行,同时到达循环结束,这就是为什么你得到相同的href。

有几种方法可以做到这一点,1个承诺(我认为是优先考虑的) - 你必须阅读有关承诺才能了解更多信息。但结帐: https://github.com/petkaantonov/bluebird http://www.html5rocks.com/en/tutorials/es6/promises/http://promise-nuggets.github.io/articles/03-power-of-then-sync-processing.html

将异步函数包装到另一个函数中并绑定任何你想要的东西(不是一个好的选择,但可能)

// wrapping your async function.
var asyncFunction = function(title,href, successCb, failCb){
    db.news.findOne({ title: title }, function(err, result){
        if (err) {
            failCb();
        } else {
            successCb()
        }
    });
};
var re = new RegExp("<a href=\"(news[^?|\"]+).*?>([^<]+)</a>", "g");
var match;
while (match = re.exec(body)){
    var href = match[1];
    var title = match[2];

    asyncFunction.call(this,title, href, function success(){}, function fail(){} );


}