2013-02-08 45 views
6

我有以下代碼:如何解決這個MongoDB/Node異步問題?

// 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條消息,說明該帳戶已經存在。

我很確定我知道這是爲什麼,我提出的解決方案是使用某種類型的隊列按順序處理每個數據庫的讀/寫操作。我想知道的是,這是否是正確的方式去做,以及對此的一般最佳實踐是什麼?

+1

所以你想要第二,第三和第四次插入失敗? –

+0

是的,因爲該帳戶應該已經存在(但不僅僅是)。 – user2037584

+3

最好的做法是在'email'上添加一個唯一的索引,然後處理'insert'錯誤,如果有一個重複的''帳戶的另一個味道已經存在。「錯誤。 – JohnnyHK

回答

10

一些語言提供了一種特殊的語言結構來處理這個問題。例如,C#具有async/await關鍵字,可讓您編寫代碼,就像調用同步API一樣。

JavaScript不會,你必須連接createAccount調用與回調。

有些人已經開發出可以幫助你組織這些代碼的庫。例如asyncstepnode-promiseQ

您還可以使用fibers庫,擴展與纖維/協同程序的JavaScript運行本機庫。

而且有些人已經擴展了結構類似於async/await語言:streamline.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
+0

另一個值得一提的選項是[tamejs](https://github.com/maxtaco/tamejs/),它與IcedCoffeeScript是由相同的開發人員 - 實際上,這是原始版本在純JS中工作。但由於某種原因,它會產生兩倍於streamline.js的代碼(儘管大約相同數量的函數),所以我建議使用streamline.js。另外,streamline.js可以讓你用try/catch更自然地處理錯誤,並且有一個纖維選項使得它更快。 –

+0

另外值得注意的是,streamline.js也可以與CoffeeScript一起使用(通過在CoffeeScript轉換之後應用Streamline.js轉換;文檔中的更多細節)。 –

-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(); 
      } 

     } 
    }); 
} 
+0

我明白爲什麼它發生了,我想要什麼知道什麼是解決問題的最佳方式。 – user2037584

+0

我添加了上面的代碼,以顯示在JavaScript中使用它的正確方式。或者,你可以使用步驟庫https://github.com/creationix/step –

+4

我傾向於投票回答這個答案的基礎上的更新說:「這是正確的方式做這個在Javascript中」。首先,關於Node.js中的異步使用,而不是Javascript,很少有一種「正確」的方式來做代碼中的任何事情。希望任何理性的開發人員都會認識到,在這種情況下,嵌套回調不會擴展,並不是理想的解決方案。我會同意這句話:「然而,嵌套的嵌套級別應該是一種代碼味道 - 有時間去思考你可以抽象成單獨的小模塊。」通過http://book.mixu.net/node/ch7.html –

0

在電子郵件中添加一個唯一的約束,您將不必檢查用戶是否存在了!