有没有更好的方法来处理Node.js / Expressjs中的嵌套callback?
我使用Node.js和Expressjs作为服务器端编码,MongoDB作为后端。 我是所有这些技术的新手。 我需要根据请求做一个动作列表。
例如在用户pipe理中
- 检查用户已经注册
- 如果注册重新发送激活电子邮件
- 如果没有注册,则从另一个表中获取userId,这将维护用户,资产等的ID。[我知道MongoDB提供独特的_id; 但我需要有一个唯一的整数ID为userId]
- 创build用户
- 发送成功或失败的回应。
为了实现这个我写了下面的代码:
exports.register = function(req,res,state,_this,callback) { switch(state) { case 1: //check user already registered or not _this.checkUser(req, res, ( state + 1 ), _this, _this.register, callback); break; case 2: //Already registered user so resend the activation email _this.resendEmail(req, res, 200, _this, _this.register, callback); break; case 3: //not registered user so get the userId from another table that will maintain the ids for user,assets etc _this.getSysIds(req, res, ( state + 2 ), _this, _this.register, callback); break; case 4: //create the entry in user table _this.createUser(req, res, ( state + 1 ), _this, _this.register, callback); break; case 200: //Create Success Response callback(true); break; case 101://Error callback(false); break; default: callback(false); break; } };
检查用户代码是这样的
exports.checkUser = function(req,res,state,_this,next,callback) { //check user already registered or not if(user) {//Already registered user so resend the activation email next(req,res,state,_this,callback); } else {//not registered user so get the userId next(req,res,(state + 1),_this,callback); } }
和其他function类似。
注册函数的第一个调用将从app.get as中执行
user.register(req,res,1,this,function(status) { //do somthing });
有没有更好的方法来做到这一点? 我面对的问题是基于一些条件,我必须遵循一系列的行动。 我可以在嵌套的callback结构中编写所有这些,但在这种情况下,我无法重用我的代码。
我的老板告诉我的一个问题是,在代码中,我调用了函数寄存器,并把它放在一堆callback函数中
状态1:chekuser
状态3:getIds
状态4:创build用户
最后在状态200,在这里,我只是退出堆栈? 这可能会导致堆栈溢出!
有没有更好的方法来处理Node.js / Expressjs中的callback?
注意:以上是一个示例代码。我有这样的许多不同的情况。
也许一个更优雅的方法就是将所有可能的函数放到一个对象中,然后根据你所处的状态调用你需要的任何一个。有点像
var States = { 1: checkuser, 2: resendEmail, 3: getSysIds, 4: createUser, 5: whateverIsNext }
那么你只需要有一个function可以完成任何事情
function exec(req,res,state,_this,callback){ States[state].apply(this, arguments) }
而且你的每一个函数都会将状态参数设置为任何它所需要的,然后调用exec
function checkUser(req,res,state,_this,callback){ var doneRight = do_whatever_you_need; state = doneRight? state++: state; //just set state to whatever number it should be exec(req,res,state,_this,callback); }
这样你就可以得到你的步骤清单,并且可读/可改变。 你有一个执行一切的函数,每一步都要pipe理下一步应该做什么。 如果你想要的话,你也可以使用named来replace已编号的步骤,这会使得它更具可读性,但是你将失去增加/减less步骤variables的能力。
你在这里混合中间件,数据库层和路由处理程序。 让我们更简单,更可重用。
首先,让我们确定,我们的中间件和路由处理程序可以相互通信。 这可以通过会话中间件完成:
// Somewhere inside configuration: app.use(express.cookieParser() app.use(express.session({secret: 'cookie monster')); // If you don't want sessions, change with this: app.use(function (req, res, next) { req.session = {}; next(); });
你现在想要的是在请求处理的任何地方都有用户信息。 现在,这只是电子邮件,是否注册。 那将是第一个独立的中间件。 为了简洁起见,假设您通过查询string中的 email
查询数据库来检测用户是否email
。
// Fills `req.session.user` with user info (email, registration). function userInfo (req, res, next) { var user = req.session.user = req.session.user || {}; user.email = req.query.email; // I'll skip validation. // I'm not sure which mongo driver you are using, so this is more of a pseudo-code. db.users.findOne({email: user.email}, function(err, bson) { if (err) { return next(err); // Express 3 will figure that error occured // and will pass control to error handler: // http://expressjs.com/guide.html#error-handling } // Could remember information from DB, but we'll just set `registred` field. user.registred = !!bson; next(); }); }
确定注册状态后,您应该发送电子邮件或创build新的用户,这将是请求处理程序( app.get
最后一个参数)。 在执行处理程序之前,我们希望我们的userInfo
运行(只能通过userInfo
(不是数组)),但我个人更喜欢这种方式):
app.get('/register', [userInfo], function (req, res) { var user = req.session.user; var fn = user.registred ? sendActivationEmail : registerUser; // If signatures differs or you need different reply for cases... // Well, you'll figure. fn(user.email, function (err) { return res.send(err ? 500 : 200); }); });
请求处理程序不应该在这两个函数内部发生什么事情(多less个数据库查询,谁在发送电子邮件以及如何)。 所有它知道的是,提供的callback的第一个参数不是null,如果错误发生。
现在关于创build新用户。 编写registerUser
函数,用于查询带有ID的表格,准备并保存DB中的用户信息。 有关如何同步这两个操作,请参阅asynchronous和其他SO问题 。 创build完成或出错时,请使用结果调用callback
。 这个函数应该是DB层的一部分。 至less,创builddb.js
模块并导出重要的东西,如registerUser
; 通过电子邮件(第一个中间件内部)查询用户信息也应该存在于DB层。
类似的,也适用于sendActivationEmail
。
边注意 :你可以把任何你想要的内部_id
在mongo documet,而不仅仅是ObjectId
。