2013-02-08 9 views
6

Mam następujący kod:Jak obejść problem asynchroniczny w 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.") 
      } 

     } 
    }); 
} 

Kiedy uruchomić skrypt po raz pierwszy, ja skończyć z 4 kont dla Boba. Kiedy uruchomię go po raz drugi, otrzymuję 4 wiadomości, że konto już istnieje.

Jestem pewien, że wiem, dlaczego tak jest, a rozwiązaniem, które wymyśliłem, jest użycie kolejki do przetwarzania każdego odczytu/zapisu bazy danych w kolejności pojedynczej. Chciałbym wiedzieć, czy jest to właściwy sposób, aby to osiągnąć i jaka byłaby najlepsza ogólna praktyka w tym zakresie?

+1

Chcesz, aby wstawki 2, 3 i 4 zakończyły się niepowodzeniem? –

+0

Tak, ponieważ konto powinno już istnieć (ale jeszcze nie jest). – user2037584

+3

Najlepszą praktyką jest dodanie unikalnego indeksu do 'e-maila', a następnie obsłużenie błędu' wstaw', jeśli istnieje duplikat, ponieważ inny smak konta "" już istnieje. "Błąd. – JohnnyHK

Odpowiedz

10

Niektóre języki zapewniają specjalną konstrukcję językową do rozwiązania tego problemu. Na przykład słowa kluczowe C# mają async/await, które umożliwiają napisanie kodu tak, jakby wywoływano synchroniczne interfejsy API.

JavaScript nie jest i musisz połączyć się z połączeniami createAccount z oddzwonień.

Niektóre osoby opracowały biblioteki, które mogą pomóc w organizacji tego kodu. Na przykład async, step, node-promise i Q

Można również korzystać z biblioteki fibers, natywną bibliotekę, która rozciąga runtime JavaScript włóknami/współprogram.

A niektórzy ludzie rozszerzony język konstruktami, które są podobne do async/await: streamline.js, IcedCoffeeScript lub wind.js. Na przykład, streamline.js (jestem autorem, więc jestem oczywiście stronniczy) wykorzystuje _ jako specjalny zwrotnej zastępczy i pozwala pisać swój przykład jako:

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

I, last but not least, nowy język funkcje takie jak generators i deferred functions są omawiane dla przyszłych wersji JavaScript (generatory są bardzo prawdopodobne, aby wylądować w ES6, odroczone funkcje wydają się być nieco zablokowane).

więc masz wiele opcji:

  • trzymać callbacków
  • użyć biblioteki pomocnika
  • używać rozszerzenia włókna wykonania
  • używać rozszerzeniem języka
  • czekać na ES6
+0

Inną opcją, o której warto wspomnieć, jest [tamejs] (https://github.com/maxtaco/tamejs/), napisana przez tych samych programistów co IcedCoffeeScript - w rzeczywistości jest to oryginalna wersja działająca w prostym JS. Ale z jakiegoś powodu generuje dwa razy tyle kodu (choć w przybliżeniu tyle samo funkcji) co streamline.js, więc polecam streamline.js. Ponadto streamline.js pozwala obsługiwać błędy bardziej naturalnie za pomocą try/catch i ma opcję włókien, która przyspiesza działanie. –

+0

Warto również zauważyć, że plik streamline.js może być również używany w języku CoffeeScript (przez zastosowanie transformacji Streamline.js po transformacji CoffeeScript, więcej szczegółów w dokumentach). –

-1

JavaScript jest asynchroniczny. accounts.findOne powraca natychmiast, więc zasadniczo wszystkie twoje 4 polecenia są wykonywane razem.

To, co robi accounts.findOne, to znajdź jeden {"email":email}, a kiedy go znajdziesz, uruchom funkcję, która jest w drugim argumencie. Następnie zwraca funkcję i przechodzi do następnej instrukcji CreateAccount. W międzyczasie, gdy wyniki są zwracane z dysku twardego (co zajmuje dużo więcej czasu niż wykonywanie tych instrukcji), przechodzi do funkcji, a ponieważ nie ma użytkownika, dodaje jeden. Ma sens?

UPDATE Jest to właściwy sposób robienia tego w 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

Rozumiem, dlaczego to się dzieje, czego chcę wiedzieć, jaki jest najlepszy sposób obejścia tego problemu. – user2037584

+0

Dodałem powyższy kod, aby pokazać, jaki jest właściwy sposób robienia tego w JavaScript. Lub możesz użyć biblioteki Step https://github.com/creationix/step –

+4

Jestem skłonny do głosowania tej odpowiedzi w oparciu o aktualizację, która mówi: "To jest właściwy sposób robienia tego w JavaScript". Przede wszystkim chodzi o używanie asynchronizmu w Node.js, a nie Javascript, rzadko istnieje jeden "właściwy" sposób na zrobienie czegokolwiek w kodzie. Mam nadzieję, że każdy racjonalny programista rozpozna, że ​​zagnieżdżone wywołania zwrotne w tym scenariuszu nie będą skalowalne i nie są tutaj idealnym rozwiązaniem. Zgodziłbym się z tym cytatem: "Jednak więcej niż kilka poziomów zagnieżdżania powinno być zapachem kodu - czas pomyśleć, co można wydzielić na osobne, małe moduły". via http://book.mixu.net/node/ch7.html –

0

Dodaj unikalne ograniczenie do wiadomości e-mail i nie musisz już sprawdzać, czy użytkownik już istnieje!

Powiązane problemy