6

次のコードがあります。

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

        }
    });
}

初めてスクリプトを実行すると、bob のアカウントが 4 つになります。2 回目に実行すると、アカウントが既に存在するという 4 つのメッセージが表示されます。

私はこれがなぜなのかを知っていると確信しており、私が思いついた解決策は、データベースの各読み取り/書き込みを一度に1つずつ処理するためにある種のキューを使用することです. 私が知りたいのは、それが適切な方法であるかどうか、そしてこれに対する一般的なベストプラクティスは何ですか?

4

3 に答える 3

10

一部の言語では、この問題に対処するための特別な言語構造が提供されています。たとえば、C# にはasync/awaitキーワードがあり、同期 API を呼び出しているかのようにコードを記述できます。

JavaScript はそうではなく、createAccount呼び出しをコールバックで連鎖させる必要があります。

このコードを整理するのに役立つライブラリを開発した人もいます。たとえば、asyncstepnode-promiseQ

また、ファイバー/コルーチンを使用して JavaScript ランタイムを拡張するネイティブ ライブラリであるファイバーライブラリを使用することもできます。

asyncまた、 /に似た構文await( streamline.jsIcedCoffeeScript、または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.")
    }
}

最後になりましたが、ジェネレーター遅延関数などの新しい言語機能が JavaScript の将来のバージョンで議論されています (ジェネレーターは ES6 に搭載される可能性が非常に高く、遅延関数は少し行き詰まっているようです)。

したがって、多くのオプションがあります。

  • コールバックに固執する
  • ヘルパー ライブラリを使用する
  • ファイバー ランタイム拡張機能を使用する
  • 言語拡張機能を使用する
  • ES6を待つ
于 2013-02-08T22:42:30.697 に答える
0

電子メールに一意の制約を追加すると、ユーザーが存在するかどうかを確認する必要がなくなります!

于 2013-02-08T05:39:46.597 に答える
-1

JavaScriptは非同期です。accounts.findOneすぐに戻るので、基本的に4つのステートメントすべてが一緒に実行されます。

accounts.findOneつまり、 1つを見つけて、それを見つけたら{"email":email}、2番目の引数にある関数を実行します。次に、関数を返し、次の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();
            }

        }
    });
}
于 2013-02-08T02:56:58.443 に答える