简体   繁体   English

node.js链多个promises(用mongoose)

[英]node.js chain multiple promises (with mongoose)

The following is a typical promise function that I am dealing with. 以下是我正在处理的典型承诺函数。

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');
    }
  });
};

This looks ok until when I am dealing with more complicated update / creates the code will become really messy. 这看起来不错,直到我处理更复杂的更新/创建代码将变得非常混乱。

Currently what I am doing with promise is that 1. I have a lot of useless return true statements and the only purpose is to go to next .then statement 2. promise are programmed in a nested style. 目前我正在承诺的是1.我有很多无用的返回真实语句,唯一的目的是转到下一个。然后声明2. promise以嵌套方式编程。 also the input arguments are usually complicated and has more then 1 arguments so that I cannot do something like this 输入参数通常很复杂,并且有超过1个参数,所以我不能做这样的事情

.then(fun1).then(fun2)

... etc ......等

which makes me unable to 'tap' the .then statement to enable/disable a functionality. 这使我无法'tap' .then语句来启用/禁用功能。

So my questions is how do I do this correctly? 所以我的问题是如何正确地做到这一点? Thanks.. 谢谢..


the following is the really ugly things that I am talking about.... 以下是我所说的非常丑陋的事情....

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

You can do two things: 你可以做两件事:

  • Unnest then callbacks Unnest then回调
  • Modularize. 模块化。 These "do product stuff" and "do inventory stuff" things might become their own functions (or even the same?). 这些“做产品的东西”和“做库存的东西”的东西可能会成为他们自己的功能(甚至是相同的?)。

In this case, unnesting could do the following (assuming you don't need closures in your commented sections): 在这种情况下,可以执行以下操作(假设您的注释部分中不需要关闭):

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

In my projects I use Async.js 在我的项目中,我使用Async.js

I think you need to decompose your _process method into small actions 我认为你需要将_process方法分解为_process

  1. Actions which depend on result from previous actions - async waterfall pattern might be used here 依赖于先前操作的结果的操作 - 异步waterfall模式可能在此处使用
  2. Actions which don't depend on the previous actions result, they may be executed in parallel 不依赖于先前动作的动作结果,它们可以并行执行
  3. Use some custom process 使用一些自定义过程

Here is an example from my app: 以下是我的应用中的示例:

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

Please refer to Async docs for a custom process, it really does contain a lot of common patterns, I also use this library in my client JavaScript. 请参阅Async文档以获取自定义过程,它确实包含许多常见模式,我也在我的客户端JavaScript中使用此库。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM