如何解决这个MongoDB / Node异步问题?

时间:2013-02-08 02:40:07

标签: javascript node.js mongodb asynchronous

我有以下代码:

// Retrieve
var MongoClient = require("mongodb").MongoClient;
var accounts = null;
var characters = null;

// Connect to the db
MongoClient.connect("mongodb://localhost:27017/bq", function(err, db) {
   if(err) { return console.dir(err); }

    db.createCollection('accounts', function(err, collection) {
        if(err) { return console.dir(err); }
        else { accounts = collection; }

        createAccount("bob","bob");
        createAccount("bob","bob");
        createAccount("bob","bob");
        createAccount("bob","bob");
    });
});


function createAccount(email, password)
{
    accounts.findOne({"email":email}, function(err, item) {
        if(err) { console.dir(err); }
        else {
            if(item === null) {
                accounts.insert({"email":email, "password":password}, function(err, result) {
                    if(err) { console.dir(err); }
                    else { console.dir("Account " + email + " created."); }
                });
            }
            else {
                console.dir("Account already exists.")
            }

        }
    });
}

当我第一次运行脚本时,我最终得到4个bob帐户。当我第二次运行它时,我收到4条帐户已存在的消息。

我很确定我知道为什么会这样,而我提出的解决方案是使用某种队列来按顺序处理数据库的每次读/写。我想知道的是,这是否是正确的方法,以及最佳做法是什么?

3 个答案:

答案 0 :(得分:10)

有些语言提供了一种特殊的语言结构来处理这个问题。例如,C#具有async / await个关键字,可让您编写代码,就像调用同步API一样。

JavaScript没有,您必须使用回调链接createAccount次调用。

有些人开发了可以帮助您组织此代码的库。例如asyncstepnode-promiseQ

您还可以使用fibers库,这是一个使用光纤/协同程序扩展JavaScript运行时的本机库。

有些人使用与async / awaitstreamline.jsIcedCoffeeScriptwind.js类似的构造扩展了语言。例如,streamline.js(我是作者所以我显然有偏见)使用_作为特殊的回调占位符,并允许您将您的示例编写为:

var db = MongoClient.connect("mongodb://localhost:27017/bq", _):
var accounts = db.createCollection('accounts', _);
createAccount("bob","bob", _);
createAccount("bob","bob", _);
createAccount("bob","bob", _);
createAccount("bob","bob", _);

function createAccount(email, password, _) {
    var item = accounts.findOne({"email":email}, _);
    if (item === null) {
        accounts.insert({"email":email, "password":password}, _);
        console.log("Account " + email + " created."); }
    } else {
        console.log("Account already exists.")
    }
}

最后但并非最不重要的是,generatorsdeferred functions等新语言功能正在为未来版本的JavaScript进行讨论(生成器很可能落在ES6中,延迟函数似乎是有点停滞。)

所以你有很多选择:

  • 坚持回调
  • 使用帮助程序库
  • 使用光纤运行时扩展
  • 使用语言扩展
  • 等待ES6

答案 1 :(得分:0)

在电子邮件中添加唯一约束,您无需再检查用户是否存在!

答案 2 :(得分:-1)

JavaScript是异步的。 accounts.findOne立即返回,所以基本上所有4个语句都会一起执行。

accounts.findOne的作用是什么,它说找到一个{"email":email},当你找到它时,运行第二个参数中的函数。然后它返回该函数并继续下一个CreateAccount语句。同时,当从硬盘驱动器返回结果时(比执行这些语句需要更长的时间),它会进入函数,并且由于没有用户,因此它会添加一个。有意义吗?

UPDATE 这是在JavaScript中执行此操作的正确方法。

MongoClient.connect("mongodb://localhost:27017/bq", function(err, db) {
   if(err) { return console.dir(err); }

    db.createCollection('accounts', function(err, collection) {
        if(err) { return console.dir(err); }
        else { accounts = collection; }

        createAccount("bob","bob", function() {
            createAccount("bob","bob", function() {
                createAccount("bob","bob", function() {
                    createAccount("bob","bob", function() {
                     });
                });
            });
        });
    });
});


function createAccount(email, password, fn)
{
    accounts.findOne({"email":email}, function(err, item) {
        if(err) { console.dir(err); }
        else {
            if(item === null) {
                accounts.insert({"email":email, "password":password}, function(err, result) {
                    if(err) { console.dir(err); }
                    else { console.dir("Account " + email + " created."); }
                    fn();
                });
            }
            else {
                console.dir("Account already exists.")
                fn();
            }

        }
    });
}