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 をクエリ コールバックに戻す必要がありました。
これを行う適切な方法はありますか?