2013-02-08 41 views
6

Tôi có đoạn mã sau:Cách khắc phục sự cố không đồng bộ của MongoDB/Node này?

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

     } 
    }); 
} 

Khi tôi chạy kịch bản lần đầu tiên, tôi kết thúc với 4 tài khoản cho bob. Khi tôi chạy nó lần thứ hai, tôi nhận được 4 thông báo rằng tài khoản đã tồn tại.

Tôi khá chắc chắn rằng tôi biết tại sao điều này xảy ra và giải pháp mà tôi đưa ra là sử dụng hàng đợi loại nào đó để xử lý từng đọc/ghi cơ sở dữ liệu theo thứ tự một lần. Những gì tôi muốn biết, liệu đó có phải là cách thích hợp để đi về nó hay không, và thực hành chung tốt nhất cho việc này là gì?

+1

Vì vậy, bạn muốn chèn lần 2, 3 và 4 không thành công? –

+0

Có, vì tài khoản đã tồn tại (nhưng chưa đến). – user2037584

+3

Cách thực hành tốt nhất là thêm một chỉ mục duy nhất trên 'email' và sau đó xử lý lỗi' chèn' nếu có lỗi song song như một hương vị khác của lỗi '" Tài khoản đã tồn tại. "'. – JohnnyHK

Trả lời

10

Một số ngôn ngữ cung cấp một cấu trúc ngôn ngữ đặc biệt để giải quyết vấn đề này. Ví dụ: C# có các từ khóa async/await cho phép bạn viết mã như thể bạn đang gọi các API đồng bộ.

JavaScript không và bạn phải chuỗi các cuộc gọi createAccount có gọi lại.

Một số người đã phát triển các thư viện có thể giúp bạn tổ chức mã này. Ví dụ async, step, node-promiseQ

Bạn cũng có thể sử dụng fibers thư viện, thư viện bản địa kéo dài thời gian chạy JavaScript với sợi/coroutines.

Và một số người đã mở rộng ngôn ngữ với các cấu trúc tương tự như async/await: streamline.js, IcedCoffeeScript hoặc wind.js. Ví dụ, streamline.js (Tôi là tác giả vì vậy tôi rõ ràng là thiên vị) sử dụng _ như một giữ chỗ gọi lại đặc biệt và cho phép bạn viết ví dụ của bạn như:

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

Và, cuối cùng nhưng không kém, ngôn ngữ mới các tính năng như generatorsdeferred functions đang được thảo luận cho các phiên bản tương lai của JavaScript (máy phát điện rất có khả năng hạ cánh trong ES6, chức năng trì hoãn có vẻ hơi bị trì hoãn).

Vì vậy, bạn có nhiều lựa chọn:

  • dính vào callbacks
  • sử dụng một thư viện helper
  • sử dụng phần mở rộng sợi runtime
  • sử dụng một phần mở rộng ngôn ngữ
  • chờ ES6
+0

Một lựa chọn khác đáng nhắc đến là [tamejs] (https://github.com/maxtaco/tamejs/), do cùng một nhà phát triển như IcedCoffeeScript - trên thực tế, đó là phiên bản gốc hoạt động trong JS đơn giản. Nhưng đối với một số lý do nó tạo ra nhiều gấp đôi mã (mặc dù xung quanh cùng một số hàm) như streamline.js, vì vậy tôi khuyên bạn nên streamline.js. Ngoài ra, streamline.js cho phép bạn xử lý các lỗi một cách tự nhiên hơn với try/catch và có tùy chọn sợi làm cho nó nhanh hơn. –

+0

Cũng đáng chú ý là streamline.js cũng có thể được sử dụng với CoffeeScript (bằng cách áp dụng biến đổi Streamline.js sau khi biến đổi CoffeeScript; chi tiết hơn trong tài liệu). –

-1

JavaScript không đồng bộ. accounts.findOne trả về ngay lập tức, vì vậy về cơ bản tất cả 4 câu lệnh của bạn đang được thực hiện cùng nhau.

Điều gì accounts.findOne là, nó nói tìm một {"email":email} và khi bạn tìm thấy, hãy chạy hàm trong đối số thứ hai. Sau đó, nó trả về hàm và tiếp tục đến câu lệnh CreateAccount tiếp theo. Trong khi đó khi các kết quả được trả về từ ổ đĩa cứng (mất nhiều thời gian hơn việc thực hiện các câu lệnh này), nó đi vào hàm, và vì không có người dùng, nó thêm một. Có ý nghĩa?

CẬP NHẬT Đây là cách phù hợp để thực hiện việc này trong 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

Tôi hiểu tại sao nó xảy ra, điều tôi muốn để biết là cách tốt nhất để làm việc xung quanh nó là gì. – user2037584

+0

Tôi đã thêm mã ở trên, để cho biết cách làm đúng cách trong JavaScript. Hoặc, bạn có thể sử dụng thư viện Bước https://github.com/creationix/step –

+4

Tôi nghiêng xuống bỏ phiếu cho câu trả lời này dựa trên nội dung cập nhật cho biết: "Đây là cách đúng để thực hiện việc này trong Javascript". Trước hết về việc sử dụng async trong Node.js, không phải Javascript, hiếm khi có một cách "đúng" để làm bất cứ điều gì trong mã. Hy vọng rằng bất kỳ nhà phát triển hợp lý nào cũng sẽ nhận ra rằng các callbacks lồng nhau trong kịch bản này sẽ không mở rộng và không phải là giải pháp lý tưởng ở đây. Tôi sẽ đồng tình với câu nói này: "Tuy nhiên, nhiều hơn một vài cấp độ làm tổ sẽ là một mùi mã - thời gian để suy nghĩ những gì bạn có thể trừu tượng ra thành các mô-đun nhỏ riêng biệt." thông qua http://book.mixu.net/node/ch7.html –

0

Thêm ràng buộc duy nhất trên email và bạn sẽ không phải kiểm tra xem người dùng có tồn tại nữa không!

Các vấn đề liên quan