简体   繁体   English

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

[英]How to get around this MongoDB/Node asynchronous issue?

I have the following code: 我有以下代码:

// 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.")
            }

        }
    });
}

When I run the script the first time, I end up with 4 accounts for bob. 当我第一次运行脚本时,我最终得到4个bob帐户。 When I run it the second time, I get 4 messages that the account already exists. 当我第二次运行它时,我收到4条消息,该帐户已经存在。

I'm pretty sure I know why this is, and the solution I have come up with is to use some kind queue for processing each read/write of the database in order one at a time. 我很确定我知道为什么会这样,而我提出的解决方案是使用某种队列来按顺序处理数据库的每次读/写。 What I am wanting to know, is whether that is the proper way to go about it, and what would the general best practice for this be? 我想知道的是,这是否是正确的解决方法,一般的最佳做法是什么?

Some languages provide a special language construct to deal with this problem. 有些语言提供了一种特殊的语言结构来处理这个问题。 For example, C# has async / await keywords that let you write the code as if you were calling synchronous APIs. 例如,C#具有async / await关键字,可让您编写代码,就像调用同步API一样。

JavaScript does not and you have to chain the createAccount calls with callbacks. JavaScript没有,您必须使用回调链接createAccount调用。

Some people have developed libraries that may help you organize this code. 有些人开发了可以帮助您组织此代码的库。 For example async , step , node-promise and Q 例如asyncstepnode-promiseQ.

You can also use the fibers library, a native library that extends the JavaScript runtime with fibers / coroutines. 您还可以使用光纤库,这是一个使用光纤/协同程序扩展JavaScript运行时的本机库。

And some people have extended the language with constructs that are similar to async / await : streamline.js , IcedCoffeeScript or wind.js . 有些人使用类似于async / await构造扩展了语言: streamline.jsIcedCoffeeScriptwind.js。 For example, streamline.js (I'm the author so I'm obviously biased) uses _ as a special callback placeholder and lets you write your example as: 例如,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.")
    }
}

And, last but not least, new language features such as generators and deferred functions are being discussed for future versions of JavaScript (generators are very likely to land in ES6, deferred functions seem to be a bit stalled). 并且,最后但并非最不重要的是,新的语言功能,如生成器延迟函数正在讨论未来版本的JavaScript(生成器很可能落在ES6中,延迟函数似乎有点停滞)。

So you have many options: 所以你有很多选择:

  • stick to callbacks 坚持回调
  • use a helper library 使用帮助程序库
  • use the fibers runtime extension 使用光纤运行时扩展
  • use a language extension 使用语言扩展名
  • wait for ES6 等待ES6

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

JavaScript is asynchronous. JavaScript是异步的。 accounts.findOne returns immediately, so basically all your 4 statements are getting executed together. accounts.findOne立即返回,所以基本上所有4个语句都在一起执行。

What accounts.findOne does is, it says find one {"email":email} and when you find it, run the function that is in the second argument. accounts.findOne所做的是,它会找到一个{"email":email} ,当你找到它时,运行第二个参数中的函数。 Then it returns the function and continues to next CreateAccount statement. 然后它返回该函数并继续下一个CreateAccount语句。 In the meanwhile when the results are returned from the harddrive (which takes a lot longer than executing these statements), it goes into the function, and since there is no user, it adds one. 同时,当从硬盘驱动器返回结果时(比执行这些语句需要更长的时间),它会进入函数,并且由于没有用户,因此它会添加一个。 Makes sense? 说得通?

UPDATE This is the right way of doing this in JavaScript. 更新这是在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();
            }

        }
    });
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM