NodeJS 更少可见的回调和更多的结构
Posted
技术标签:
【中文标题】NodeJS 更少可见的回调和更多的结构【英文标题】:NodeJS less visible callbacks and more structure 【发布时间】:2012-10-11 01:28:51 【问题描述】:NodeJS 的一个优点是它的异步和非阻塞 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(节点验证器的中间件) 护照(身份验证中间件)检查用户给出的输入是否有效是最小的问题,我有这个检查我在哪里断言变量并将页面的渲染版本返回给用户打印出错误。
但是如果我们首先通过验证错误,我们假设“大厅”已准备好插入数据库,然后我想确保用户没有其他大厅打开并且不是另一个大厅的成员。 好吧,现在我最终将一个查询放入另一个查询中,理论上我必须将我的视图渲染函数(res.render())放入每个查询回调中,如果查询遇到错误或返回指示用户不被允许的结果创建一个大厅。 我不想那样做,而且似乎不太实用。
我尝试从查询回调中删除渲染逻辑和所有其他逻辑,而是让查询回调设置指示成功或失败的错误数组或变量,并在我的查询代码下方检查是否(错误)renderPageWithErrors。
由于 nodejs 的异步行为,这会导致奇怪的错误,在这种情况下 res.redirect() 是在 res.render() 之后调用的,类似的东西。 我不得不将我的 res.render 移回查询回调中。
有合适的方法吗?
【问题讨论】:
【参考方案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
);
它所做的另一件好事是将所有错误合并到一个回调中。这样一来,您只需在一个地方处理错误,而不是在整个代码中散布错误。
【讨论】:
【参考方案2】:您可能还想检查https://github.com/0ctave/node-sync 库。它是 nodejs Fibers 的语法糖,一种在不破坏 nodejs 事件循环模型的情况下以传统方式编写异步代码的方法。关于使用 Fibers 的利弊有很多讨论,但我更喜欢代码可读性和易于开发,而不是潜在的少量资源使用增加。
我不知道你所有的代码逻辑,但上面的函数可能看起来像这样:
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
);
【讨论】:
我选择了异步,因此我觉得它更像 NodeJS。以上是关于NodeJS 更少可见的回调和更多的结构的主要内容,如果未能解决你的问题,请参考以下文章