如何解决这个MongoDB / Nodeasynchronous问题?

我有以下代码:

// 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个账号。 当我第二次运行它时,我得到4个消息,该帐户已经存在。

我很确定我知道这是为什么,我提出的解决scheme是使用某种types的队列按顺序逐个处理数据库的每个读/写操作。 我想知道的是,这是否是正确的方式去做呢,一般的最佳做法是什么呢?

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

JavaScript不,你必须链接createAccount调用与callback。

有些人开发了可以帮助你组织这些代码的库。 例如async , step , node-promise和Q

您也可以使用纤维库,这是一个本地库,它使用纤维/协同程序扩展JavaScript运行时。

有些人已经用类似于async / await结构扩展了语言: streamline.js , IcedCoffeeScript或者wind.js。 例如,streamline.js(我是作者,所以我显然有偏见)使用_作为一个特殊的callback占位符,并让你写下你的例子:

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

最后但并非最不重要的是,未来版本的JavaScript(发电机很可能着陆在ES6中,延期function似乎有点停顿)正在讨论新的语言function,如发电机和延期function 。

所以你有很多select:

  • 坚持callback
  • 使用助手库
  • 使用光纤运行时扩展
  • 使用语言扩展
  • 等待ES6

在电子邮件上添加一个唯一的约束,你将不必检查用户是否存在了!

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

accounts.findOne做的是,find一个{"email":email} ,当你find它的时候,运行第二个参数中的函数。 然后它返回该函数并继续下一个CreateAccount语句。 与此同时,从硬盘返回结果(比执行这些语句要花费更多的时间),它会进入函数,并且由于没有用户,所以会添加一个。 说得通?

更新这是在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(); } } }); }