3

NodeJS の利点の 1 つは、その非同期でノンブロッキングの I/O です。これは、私の場合は素晴らしいことですが、毎日首を痛めています。

私は自分自身を NodeJS / Async の初心者だと考えており、そのようなコードを作成することがよくあります。

function(req, res) {
            req.assert("name", "Lobbyname is required").notEmpty();
            req.assert("name", "Lobbyname length should be between 4 and 64 characters").len(4, 64);
            req.assert("game", "Game not found").isInt();

            req.sanitize("game").toInt();
            var userId = req.user.id;

            var errors = req.validationErrors();
            var pg_errors = [];
            var games = null;
            if (errors) {
                console.log(errors);
                client.query("SELECT * FROM games", function(err, result) {
                    if (!err) {
                        games = result.rows;
                        res.render("lobby/create", {
                            title: "Create a new lobby",
                            games: games,
                            errors: errors.toString()
                        });
                    }
                    else {
                        res.send("error");
                    }
                });
            }
            else {
                errors = null;
                client.query("SELECT COUNT(*) as in_lobbies FROM users u RIGHT JOIN lobby_userlist ul ON ul.user_id = u.id WHERE u.id = $1", [userId], function(err, result) {
                    if (!err) {
                        console.log(result.rows[0]);
                        if (result.rows[0].in_lobbies < 1) {
                            client.query("SELECT COUNT(*) as hosting_lobbies FROM lobbies WHERE owner = $1", [userId], function(err, result) {
                                if (!err) {
                                    if (result.rows[0].hosting_lobbies < 1) {
                                        client.query("INSERT INTO lobbies(name, game, owner) VALUES($1, $2, $3)", [req.param("name"), req.param("game"), userId], function(err, result) {
                                            if (!err) {
                                                res.redirect("/lobby");
                                            }
                                            else {
                                                pg_errors.push(err);
                                                console.log(err);
                                            }
                                        });
                                    }
                                    else {
                                        errors = "You can only host one lobby at a time";
                                    }
                                }
                                else {
                                    pg_errors.push(err);
                                    client.query("SELECT * FROM games", function(err, result) {
                                        if (!err) {
                                            games = result.rows;

                                            res.render("lobby/create", {
                                                title: "Create a new lobby",
                                                games: games,
                                                errors: errors
                                            });
                                        }
                                        else {
                                            pg_errors.push(err);
                                        }
                                    });
                                }
                            });
                        }
                        else {
                            pg_errors.push(err);
                        }
                    }
                });

                console.log("pg_errors");
                console.log(pg_errors);
                console.log("pg_errors _end");

                if (pg_errors.length < 1) {
                    console.log("no errors");
                }
                else {
                    console.log(pg_errors);
                    res.send("error service operation failed");
                }
            }
        }

これは、次の npm パッケージを使用して作成した例です。

  • pg (ネイティブ)
  • 特急
  • express-validator (node-validator のミドルウェア)
  • パスポート(認証ミドルウェア)

ユーザーによって与えられた入力が有効かどうかを確認することは、最も問題が少ないことです。変数をアサートし、ユーザーにエラーを出力するページのレンダリングされたバージョンを返す場所を確認します。

しかし、最初に検証エラーに合格した場合、ユーザーが他のロビーを開いておらず、別のロビーのメンバーではないことを確認する前に、「ロビー」をデータベースに挿入する準備ができていると想定します。さて、あるクエリを別のクエリに入れることになり、理論的には、クエリでエラーが発生した場合、またはユーザーが許可されていないことを示す結果が返された場合、ビューレンダリング関数 (res.render()) をすべてのクエリコールバックに配置する必要があります。ロビーを作成します。私はそれを望んでおらず、あまり実用的ではないようです。

レンダー ロジックと他のすべてのロジックをクエリ コールバックから削除しようとしましたが、代わりに、クエリ コールバックに成功または失敗を示すエラー配列または変数を設定させ、クエリ コードの下で if(errors) renderPageWithErrors をチェックしました。

これは、nodejs の非同期動作による奇妙なエラーにつながります。この場合、res.redirect() は res.render() などの後に呼び出されます。res.render をクエリ コールバックに戻す必要がありました。

これを行う適切な方法はありますか?

4

2 に答える 2

1

asyncなどのライブラリを調べるとよいでしょうhttps://github.com/caolan/async。このような混乱にならないように、非同期コードを構造化するのに役立ちます。seriesシンプルでparallel実行可能なものwaterfallからauto、依存関係の追跡を行うものまで、要件に応じてさまざまな方法があります。

async.auto({
    get_data: function(callback){
        // async code to get some data
    },
    make_folder: function(callback){
        // async code to create a directory to store a file in
        // this is run at the same time as getting the data
    },
    write_file: ['get_data', 'make_folder', function(callback){
        // once there is some data and the directory exists,
        // write the data to a file in the directory
        callback(null, filename);
    }],
    email_link: ['write_file', function(callback, results){
        // once the file is written let's email a link to it...
        // results.write_file contains the filename returned by write_file.
    }]
}, function(err) { 
    // everything is done or an error occurred 
});

それが行うもう 1 つの優れた点は、すべてのエラーを 1 つのコールバックに統合することです。そうすれば、コード全体に散らばるエラーではなく、1 か所でエラーを処理するだけで済みます。

于 2012-10-20T18:20:31.690 に答える
1

https://github.com/0ctave/node-syncライブラリも確認することをお勧めします。これは、nodejsファイバーのシンタックス シュガーであり、nodejs イベント ループ モデルを壊すことなく、従来の方法で非同期コードを記述する方法です。ファイバーを使用することの長所と短所については多くの議論がありますが、私はリソース使用量がわずかに増加する可能性よりも、コードの読みやすさと開発の容易さを好みます。

あなたのコードロジックのすべてを知っているわけではありませんが、上記の関数は次のようになります。

function(req, res) {
    Sync(function() {
        req.assert("name", "Lobbyname is required").notEmpty();
        req.assert("name", "Lobbyname length should be between 4 and 64 characters").len(4, 64);
        req.assert("game", "Game not found").isInt();

        req.sanitize("game").toInt();
        var userId = req.user.id;

        var errors = req.validationErrors();
        var pg_errors = [];
        var games = null;
        if (errors) {
            console.log(errors);
            var games = client.query.sync(client, "SELECT * FROM games").rows;
            games = result;
            res.render("lobby/create", {
                title: "Create a new lobby",
                games: games,
                errors: errors.toString()
            });
        }
        else {
            errors = null;
            var result = client.query.sync(client, "SELECT COUNT(*) as in_lobbies FROM users u RIGHT JOIN lobby_userlist ul ON ul.user_id = u.id WHERE u.id = $1", [userId]);

            console.log(result.rows[0]);
            if (result.rows[0].in_lobbies < 1) {
                var result = client.query.sync(client, "SELECT COUNT(*) as hosting_lobbies FROM lobbies WHERE owner = $1", [userId]);

                if (result.rows[0].hosting_lobbies < 1) {
                    var res = client.query.sync(clien, "INSERT INTO lobbies(name, game, owner) VALUES($1, $2, $3)", [req.param("name"), req.param("game"), userId]);
                    res.redirect("/lobby");
                }
                else {
                    errors = "You can only host one lobby at a time";
                }
            }
            else {
                var games = client.query.sync(client, "SELECT * FROM games").rows;

                res.render("lobby/create", {
                    title: "Create a new lobby",
                    games: games,
                    errors: errors
                });
            };
        }
    }, function(err) {
        if(err) {
            // do your error handling here
        }
    });
}
于 2012-10-20T19:45:27.900 に答える