在这种情况下如何避免回调地狱?

时间:2017-04-07 05:31:35

标签: javascript node.js asynchronous callback

我有以下代码,你可以看到,函数内部的每个函数都依赖于封闭函数的返回值。但问题是当我继续使用这种方法进行编码时,会发生回调地狱。如何避免这种地狱?

User.getUserDetail(req.user.id, function(userDetail) {
       if(req.user.entity_id != '-1') {
           Entity.getEntityPrimary(req.user.entity_id, function(entityPrimary) {
               Entity.getEntityPayment(req.user.entity_id, function(entityPayment) {
                   if(entityPayment.length > 0) {
                       Entity.subscriptionInfo(entityPayment[0]['date'], entityPayment[0]['exp_date'], function(isSubscribed) {
                           res.render('capitol', {
                               user: req.user,
                               title: 'MCIC',
                               user_detail: userDetail,
                               entity_primary: entityPrimary,
                               entity_payment: entityPayment,
                               subscriber: true,
                               is_subscribed: isSubscribed
                           })
                       })
                   } else {
                       res.render('capitol', {
                           user: req.user,
                           title: 'MCIC',
                           user_detail: userDetail,
                           entity_primary: entityPrimary,
                           entity_payment: entityPayment,
                           subscriber: false
                       })
                   }
               })
           })
       } else {
           res.render('capitol', {
               user: req.user,
               title: 'MCIC',
               user_detail: userDetail
           })
       }
   })

模型文件如下

const MySql = require('../comms/mysql')
const User = module.exports = {}

User.getUserByUsername = function(username, callback) {
    MySql.connection.query('SELECT id, username, password, is_active, email, mobile, user_type_id, entity_id FROM `user` WHERE `username` = ?', username, function(err, rows, fields) {
        callback(rows)
    })
}

User.getUserById = function(id, callback) {
    MySql.connection.query('SELECT id, username, password, is_active, email, mobile, user_type_id, entity_id FROM `user` WHERE `id` = ?', id, function(err, rows, fields) {
        callback(err, rows)
    })
}

User.getUserDetail = function(id, callback) {
    MySql.connection.query('SELECT first_name, last_name, dob, address_no, address_street, address_district, address_postal_code, profile_picture, user_id FROM `user_detail` WHERE `user_id` = ?', id, function(err, rows, fields) {
        callback(rows)
    })
}

我在生产网站上使用此代码。如何轻松地从回调地狱过渡到结构良好的代码?

4 个答案:

答案 0 :(得分:0)

使用JavaScript承诺

step1(function (value1) {
    step2(value1, function(value2) {
        step3(value2, function(value3) {
            step4(value3, function(value4) {
                // Do something with value4
            });
        });
    });
});

可以制作

Q.fcall(promisedStep1)
.then(promisedStep2)
.then(promisedStep3)
.then(promisedStep4)
.then(function (value4) {
    // Do something with value4
})
.catch(function (error) {
    // Handle any error from all above steps
})

在此处阅读更多Link to Q.js

答案 1 :(得分:0)

要获得快速奖励,您可以将回调函数解压缩为模块级别的命名函数:

// Before: Deeply nested

asyncOperationA(function(resA) {
  asyncOperationB(resA, function(resB) {
    asyncOperationC(resB, function(resC) {
      console.log(resC)
    })
  })
})


// After: flattened

function handleA(resA) {
  asyncOperationB(handleB)
}

function handleB(resB) {
  asyncOperationC(handleC)
}

function handleC(resC) {
  console.log(resC)
}

asyncOperationA(handleA)

更好的选择是修改您的代码以使用promises。使用async / await关键字可获得最大的可读性。要使用async / await,您需要拥有相当新的Node版本或使用转换器将其转换为vanilla JavaScript。

答案 2 :(得分:0)

使用async await

async function() {
  const userDetail = await User.getUserDetail(req.user.id);

  if (req.user.entity_id != '-1') {
    // you can batch the two into simulatenous queries (see below)
    const entityPrimary = await Entity.getEntityPrimary(req.user.entity_id);
    const entityPayment = await Entity.getEntityPayment(req.user.entity_id);

    if (entityPayment.length > 0) {
      const isSubscribed = await Entity.subscriptionInfo(entityPayment[0]['date'], entityPayment[0]['exp_date']);
      res.render('capitol', {
        user: req.user,
        title: 'MCIC',
        user_detail: userDetail,
        entity_primary: entityPrimary,
        entity_payment: entityPayment,
        subscriber: true,
        is_subscribed: isSubscribed
      });
    } else {
      res.render('capitol', {
        user: req.user,
        title: 'MCIC',
        user_detail: userDetail,
        entity_primary: entityPrimary,
        entity_payment: entityPayment,
        subscriber: false
      });
    }
  } else {
    res.render('capitol', {
      user: req.user,
      title: 'MCIC',
      user_detail: userDetail
    });
  }
}

我使用这种模式。我使用了Babel,但还有其他选项可以获得异步等待支持。我碰巧使用PostgreSQL和pg-promise,我的同步查询看起来像这样:

const results = await db.tx(t => t.batch([query1, query2]));

答案 3 :(得分:0)

你可以使用像这样的async / await来删除回调地狱

const Promise = require('bluebird');
const User = Promise.promisifyAll(require('PATH_TO_USER'));
const Entity = Promise.promisifyAll(require('PATH_TO_ENTITY'));

async function yourController(req, res, next) {
  try {
    const userDetail = await User.getUserDetail(req.user.id);
    if(req.user.entity_id !== -1) {
      const entityPrimary = await Entity.getEntityPrimary(req.user.entity_id);
      const entityPayment = await Entity.getEntityPayment(req.user.entity_id);

      if(entityPayment.length > 0) {
        const isSubscribed = await Entity.subscriptionInfo(entityPayment[0]['date'], entityPayment[0]['exp_date']);
        return res.render('capitol', {
          user: req.user,
          title: 'MCIC',
          user_detail: userDetail,
          entity_primary: entityPrimary,
          entity_payment: entityPayment,
          subscriber: true,
          is_subscribed: isSubscribed
        })
      } else {
          return res.render('capitol', {
            user: req.user,
            title: 'MCIC',
            user_detail: userDetail,
            entity_primary: entityPrimary,
            entity_payment: entityPayment,
            subscriber: false
          })
      }
    } else {
        return res.render('capitol', {
          user: req.user,
          title: 'MCIC',
          user_detail: userDetail
        })
    }
  } catch(e) {
    // handle exception here
  }
}

步骤

  • async关键字添加到您的控制器,例如async function yourController(req, res, next)
  • 使用bluebird
  • 包装模型模块
  • 使用await
  • 调用模型