NodeJS如何在控制器中组织回调

时间:2014-03-14 00:34:09

标签: javascript node.js sails.js asynccallback

我是NodeJS的新手,来自PHP环境我试图弄清楚如何使用多个回调。我确实理解了关于回调的基础知识,我认为在编写模块时它确实很有意义。我的问题是什么时候来使用这些模块如何组织所有的回调。下面是我的实现请求重置密码控制器方法(我正在使用SailsJS)。这是我的代码的初稿。它主要是测试一种组织回调的方法。你们怎么看待这个结构?有更好的方法吗?

var _ = require('lodash'); 
var moment = require('moment');
var mailer = require("../../services/Mailer");
var crypto = require('../../services/crypto');
var forms = require("forms"),
    fields = forms.fields,
    validators = forms.validators;

module.exports = {
  // Request reset user password: submit form and send email
  request_process : function(req, res, next) {

    var form = createForm();

    form.handle(req, {

        // there is a request and the form is valid
        // form.data contains the submitted data
        success: function (form) {
            var user = null;
            var username = form.data.username;

            User.findOne({'username' : username}, foundUser);

            function foundUser( err, _user){
                if(err)
                    res.send(500, 'User not found');

                user = _user;

                if user.isPasswordRequestNonExpired()
                    return res.view('user/resetting/passwordAlreadyRequested');

                if !user.passwordRequestToken
                    user.passwordRequestToken = crypto.randomToken();

                renderEmail(null, user);
            }

            function renderEmail(err, user){
                res.render('email/resetting_check_email', {'user': user, }, sendEmail );
            }

            function sendEmail(err, template){
                if(err)
                    return res.send(500, "Problem with sending email");

                Mailer.send( user, "Reset Password", template, sentEmail);
            }

            function sentEmail(err, response){
                if(err)
                    return res.send(500, "Error sending email");

                user.passwordRequestedAt = moment().format();
                user.save(finish);
            }

            function finish(err){
                if(err)
                    return res.send(500);

                res.view();
            }               
        },

        // the data in the request didn't validate,
        // calling form.toHTML() again will render the error messages
        error: function (form) {
            console.log("registration error", form);

            res.locals.form = form;
            return res.render('user/registration/register');
        },

        // there was no form data in the request
        empty: function (form) {
            console.log("registration empty", form);

            res.locals.form = form;
            return res.render('user/registration/register');
        }
},

// Tell the user to check his email provider
check_email : function(req, res, next) {
    // if empty req.params.email
        // redirect request view

    // res.view('check_email.ejs')
},

// Reset user password
reset : function(req, res, next){
    // find userByPasswordToken

    // if !user
        // res.view ('invalid or expired "confirmation token".')

    // user.update password

    // res.view('reset.ejs');
},

2 个答案:

答案 0 :(得分:2)

Node.js回调基础:

大多数功能(节点及其库(称为模块))具有异步(异步)特性。

这些函数有一个共同的签名,回调作为最后一个参数:function(arguments.....callback)

callback只是另一个JavaScript函数。 (是的,在Javascript中,函数可以作为参数传递给其他函数)。 Node.js典型的回调有一个签名,第一个参数为错误(如果有的话):callback(err,outputs......)

示例:第一个参数是一个字符串,第二个是对象(内联定义),最后一个是函数(内联定义)。

doSomeWork('running',{doFast:true,repeat:20}, function(err,result){
  if(err){
    console.log('ohnoes!);
  } else {
    console.log('all done : %s',result);
  }
});

相当于:

var type = 'running';
var options = {doFast:true,repeat:20};

var callback = function(err,result){
  if(err){
    console.log('ohnoes!);
  } else {
    console.log('all done : %s',result);
  }
};

doSomeWork(type,options,callback);

所以这里的基本契约是给函数一个参数,并在完成后传递一个回调函数。传递的回调将在以后有什么地方可以调用时返回,错误或结果。

多个嵌套回调通常不太可读和复杂:

function uploadAll(path,callback){
  listFiles(path,function(err,files){
    if(err){
      callback(err);
    }else{
      var uploaded = [];
      var error;
      for(var i = 0 ; i < files.length; i++){
        uploadFile(files[i],function(err,url){
          if(err){
            error = err;
            break;
          }else{
            uploaded.push(url);
          }
        });
      }
      callback(error,uploaded);
    }
  }); 
};

但幸运的是,有async这样的模块可以帮助组织回调:

function uploadAll(path,callback){
  async.waterfall(
   [
     function(cb){
       listFiles(path,cb);
     },

     function(files,cb){
       async.map(files,uploadFile,cb);
     }

   ],callback);
}

此外,还有Promises模式。未来版本支持generators,它提供了许多新的异步模式。

答案 1 :(得分:1)

您可以使用asyncq来管理回调金字塔