我有以下代码:
// 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条帐户已存在的消息。
我很确定我知道为什么会这样,而我提出的解决方案是使用某种队列来按顺序处理数据库的每次读/写。我想知道的是,这是否是正确的方法,以及最佳做法是什么?
答案 0 :(得分:10)
有些语言提供了一种特殊的语言结构来处理这个问题。例如,C#具有async
/ await
个关键字,可让您编写代码,就像调用同步API一样。
JavaScript没有,您必须使用回调链接createAccount
次调用。
有些人开发了可以帮助您组织此代码的库。例如async,step,node-promise和Q
您还可以使用fibers库,这是一个使用光纤/协同程序扩展JavaScript运行时的本机库。
有些人使用与async
/ await
:streamline.js,IcedCoffeeScript或wind.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.")
}
}
最后但并非最不重要的是,generators和deferred functions等新语言功能正在为未来版本的JavaScript进行讨论(生成器很可能落在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();
}
}
});
}