簡體   English   中英

有沒有更好的方法來處理Node.js / Expressjs中的嵌套回調?

[英]Is there any better way to handle the nested callbacks in Node.js/Expressjs?

我將Node.js和Expressjs用於服務器端編碼,並將MongoDB作為后端。 我是所有這些技術的新手。 我需要根據請求列出操作列表。

例如在用戶管理中

  1. 檢查已經注冊的用戶
  2. 如果已注冊,請重新發送激活電子郵件
  3. 如果未注冊,則從另一個表獲取userId,該表將維護用戶,資產等的ID。[我知道MongoDB提供了唯一的_id; 但是我需要有一個唯一的整數id作為userId]
  4. 創建用戶
  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);
    }
}

和其他功能類似。

第一次調用register函數將在app.get中完成

user.register(req,res,1,this,function(status) {
//do somthing
});

有什么更好的方法嗎? 我面臨的問題是基於某些條件,我必須采取一系列措施。 我可以在嵌套的回調結構中編寫所有這些代碼,但是在那種情況下,我無法重用我的代碼。

老板告訴我的問題之一是,在代碼中,我正在調用函數寄存器,並將其放入回調堆棧中,如下所示:

狀態1:chekuser

狀態3:getIds

狀態4:創建用戶

最后進入狀態200,在這里我只是從堆棧中退出? 可能導致堆棧溢出!

有沒有更好的方法來處理Node.js / Expressjs中的回調?

注意:以上是示例代碼,我有很多類似的情況。

也許更優雅的方法是將所有各種可能的函數放到一個對象中,然后根據所處的狀態調用所需的任何一個。

var States = {
   1: checkuser,
   2: resendEmail,
   3: getSysIds,
   4: createUser,
   5: whateverIsNext 
}

然后,您只需要一個功能即可完成所有工作

function exec(req,res,state,_this,callback){
   States[state].apply(this, arguments) 
}

而且您的每個函數都會將state參數設置為所需的值,然后調用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);
}

這樣,您就可以輕松,易懂地更改步驟列表。 您已經擁有一個執行所有功能的函數,並且每個步驟僅需管理下一步應該做什么。 如果需要,也可以將已編號的步驟替換為named,這將使其更具可讀性,但是您將失去增加/減少步驟變量的能力。

您在這里過度設計,混合了中間件,數據庫層和路由處理程序。 讓我們使其更簡單,更可重用。

首先,請確保我們的中間件和路由處理程序可以相互通信。 這可以通過會話中間件來實現:

// 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();
});

您現在想要的是在請求處理期間到處都有用戶信息。 現在,它只是電子郵件以及是否已注冊。 那將是第一個獨立的中間件。 為簡便起見,我們假設您通過使用來自查詢字符串的 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();
  });
}

確定注冊狀態后,您應該發送電子郵件或創建新用戶,該用戶將成為請求處理程序( 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);
  });
});

請求處理程序不應被這兩個函數內部的內容所困擾(有多少數據庫查詢,誰在發送電子郵件以及如何發送)。 它所知道的是,如果發生錯誤,則提供的回調的第一個參數不為null。

現在開始創建新用戶。 編寫registerUser函數,該函數將查詢具有ID的表,准備用戶信息並將其保存在DB中。 有關如何同步這兩個操作的信息,請參見async和其他SO問題 創建完成或出錯時,調用帶有結果的callback 此功能應該是數據庫層的一部分。 至少,創建db.js模塊並導出重要的東西,例如registerUser 通過電子郵件查詢用戶信息(在第一個中間件中)也應該存在於DB層中。

類似的適用於sendActivationEmail

注意 :您可以將所需的任何內容放在mongo documet的_id中,而不僅僅是ObjectId

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM