node.js链多个promises(用mongoose)

时间:2015-01-14 07:36:09

标签: javascript node.js promise bluebird

以下是我正在处理的典型承诺函数。

var _delete = function(t, id) { 
  return Promise.cast(Event.find({where: {id: id}}, {transaction: t}))
  .then(function(d){
    if (d) {
      // ------- (*)
      return Promise.cast(d.updateAttributes({status: -1}, {transaction: t}))
      .then(function(){
          // do inventory stuff 
          return Promise.cast(Inventory.update({}).exec())
          .then(function(d){
               // do something 
          })
      }).then(function(){
          // do product stuff
          return Promise.cast(Product.update({}).exec())
          .then(function(d){
               // do something 
          })
      })
    } else {
      return Promise.reject('this transaction list does not exist');
    }
  });
};

这看起来不错,直到我处理更复杂的更新/创建代码将变得非常混乱。

目前我所做的承诺是 我有很多无用的返回真实陈述,唯一的目的是去下一个.then陈述 2.承诺以嵌套方式编程。输入参数通常很复杂,并且有超过1个参数,所以我不能做这样的事情

.then(fun1).then(fun2)

...等

这使我无法'tap' .then语句来启用/禁用某项功能。

所以我的问题是如何正确地做到这一点?感谢..


以下是我所说的非常丑陋的事情......

var _process = function(t, tid) {
  var that = this;
  return Promise.cast(Usermain.find({where: {transaction_id: tid}}))
  .bind({})  // --- (*)
  .then(function(d){
    this.tmain = d;
    return true;   // ---- do nothing, just go to next thennable (is this correct)
  }).then(function(){
    return Promise.cast(Userlist.findAndCountAll({where: {transaction_id: tid}}))
  }).then(function(d){
    this.tlist = d;
    return true;  // ---- do nothing, just go to next thennable (is this correct)
  }).then(function(){
    if (this.tmain.is_processed) {
      return Promise.reject('something is wrong');
    }
    if (this.tlist.count !== this.tmain.num_of_tran) {
      return Promise.reject('wrong');
    }
    return Promise.resolve(JSON.parse(JSON.stringify(this.tlist.rows)))
    .map(function(d){
      if (d.is_processed) return Promise.reject('something is wrong with tran list');
      return true;  // goto next then
    });
  }).then(function(){
    return Promise.cast(this.tmain.updateAttributes({is_processed: 1}, {transaction: t}));
  }).then(function(){
    return Promise.resolve(this.tlist.rows)
    .map(function(d){
      var tranlist = JSON.parse(JSON.stringify(d));
      return Promise.cast(d.updateAttributes({is_processed: 1, date_processed: Date.now()}, {transaction: t}))
      .then(function(d){
        if (!d) {
          return Promise.reject('cannot update tran main somehow');
        } else {
            if (tranlist.amount < 0) {
              return Usermoney._payBalance(t, tranlist.user_id, -tranlist.amount);
            } else {
              return Usermoney._receiveBalance(t, tranlist.user_id, tranlist.amount);
            }
          }
      });
    });
  });
}

2 个答案:

答案 0 :(得分:9)

你可以做两件事:

  • Unnest then callbacks
  • 模块化。这些“做产品的东西”和“做库存东西”的东西可能会成为他们自己的功能(甚至是同样的?)。

在这种情况下,可以执行以下操作(假设您的注释部分中不需要关闭):

function _delete(t, id) { 
    return Promise.cast(Event.find({where: {id: id}}, {transaction: t}))
    .then(function(d){
        if (d) {
            return Promise.cast(d.updateAttributes({status: -1}, {transaction: t}));
        else
            throw new Error('this transaction list does not exist');
    })
    .then(function(){
        // do inventory stuff 
        return Promise.cast(Inventory.update({}).exec())
    })
    .then(function(d){
        // do something 
    })
    .then(function(){
        // do product stuff
        return Promise.cast(Product.update({}).exec())
    })
    .then(function(d){
        // do something 
    });
}

答案 1 :(得分:2)

在我的项目中,我使用Async.js

我认为您需要将_process方法分解为小动作

  1. 依赖于先前操作的结果的操作 - 此处可能使用异步waterfall模式
  2. 不依赖于先前操作结果的操作,它们可以并行执行
  3. 使用一些自定义流程
  4. 以下是我的应用中的示例:

    async.waterfall([
    
        function findUser(next) {
            Users.findById(userId, function (err, user){
                if(err) {
                    next(new Error(util.format('User [%s] was not found.', userId)));
                    return;
                }
    
                next(null, user);
            });
        },
    
        function findUserStoriesAndSurveys(user, next) {
    
            async.parallel([
                function findStories(callback) {
                    // find all user stories
    
                    Stories.find({ UserGroups: { $in : user.Groups } })
                        .populate('Topic')
                        .populate('Episodes')
                        .exec(function(err, stories) {
                            if(err) {
                                callback(err);
                                return;
                            }
    
                            callback(null, stories);
                        });
                },
                function findSurveys(callback) {
                    // find all completed surveys
    
                    Surveys.find({
                        User: user
                    }).exec(function(err, surveys) {
                        if(err) {
                            callback(err);
                            return;
                        }
    
                        callback(null, surveys);
                    });
                }
            ],
            function(err, results) {
                if(err) {
                    next(err);
                    return;
                }
    
                next(null, results[0], results[1]);
            });
        },
    
        function calculateResult(stories, surveys, next) {
    
            // do sth with stories and surveys
    
            next(null, { /* result object */ });
        }
    
    ], function (err, resultObject) {
        if (err) {
            res.render('error_template', {
                status: 500,
                message: 'Oops! Server error! Please reload the page.'
            });
        }
    
        res.send(/* .... */);
    });
    

    请参阅Async文档以获取自定义过程,它确实包含许多常见模式,我也在我的客户端JavaScript中使用此库。