有没有更好的方法来处理Node.js / Expressjs中的嵌套callback?

我使用Node.js和Expressjs作为服务器端编码,MongoDB作为后端。 我是所有这些技术的新手。 我需要根据请求做一个动作列表。

例如在用户pipe理中

  1. 检查用户已经注册
  2. 如果注册重新发送激活电子邮件
  3. 如果没有注册,则从另一个表中获取userId,这将维护用户,资产等的ID。[我知道MongoDB提供独特的_id; 但我需要有一个唯一的整数ID为userId]
  4. 创build用户
  5. 发送成功或失败的回应。

为了实现这个我写了下面的代码:

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