如何解决这个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(); } } }); }