0

This below is my snippet code of sailsJS. This code is work on me.

afterCreate: function( value, callback ) {
    var getStock = function( cb ) {
      PurchaseInvoiceDetail
        .find({ product: value.product.id })
        .then(function( detail ) {
          cb(null, SharedInvoiceService.sumNumberArray(_.pluck(detail, 'quantity')));
        });
    };

    var updateStock = ['getStock', function( results, cb ) {
      Product.findOne(value.product.id).then(function( product ) {
        product.stock = results.getStock;
        product.save(cb);
      });
    }];

    async.auto({
      getStock: getStock,
      updateStock: updateStock
    }, function( results ) {
      console.log(results);
      callback();
    });
});

Then, I try to wrap that code for doing iteration like this

afterCreate: function( value, callback ) {
    _.each(value.details, function(valueDetail) {
        // wrapped
        var getStock = function( cb ) {
          PurchaseInvoiceDetail
            .find({ product: valueDetail.product.id })
            .then(function( detail ) {
              cb(null, SharedInvoiceService.sumNumberArray(_.pluck(detail, 'quantity')));
            });
        };

        var updateStock = ['getStock', function( results, cb ) {
          Product.findOne(valueDetail.product.id).then(function( product ) {
            product.stock = results.getStock;
            product.save(cb);
          });
        }];

        async.auto({
          getStock: getStock,
          updateStock: updateStock
        }, function( results ) {
          console.log(results);
          callback();
        });
        // end-of-wrapped
    });

});

And it doesn't work like I expected. So, how to operate each on async.auto?

UPDATE

Scott Gress' answer is more make sense and I've try it, but I got new problem because sometimes value.details has no value. I modify my code like that.

beforeCreate: function( value, callback ) {
  if (value.details) { // make sure value.details is defined
    console.log('called');
    async.each(value.details, function(valueDetail, eachCallback) {
      // wrapped
      var getStock = function( cb ) {
        PurchaseInvoiceDetail
          .find({ product: valueDetail.product.id })
          .then(function( detail ) {
            cb(null, SharedInvoiceService.sumNumberArray(_.pluck(detail, 'quantity')));
          });
      };

      var updateStock = ['getStock', function( results, cb ) {
        Product.findOne(valueDetail.product.id).then(function( product ) {
          product.stock = results.getStock;
          product.save(cb);
        });
      }];

      async.auto({
        getStock: getStock,
        updateStock: updateStock
      }, function( results ) {
        console.log(results);
        eachCallback();
      });
      // end-of-wrapped
    }, callback);
  } else {
    console.log('skipped');
    callback();
  }

},

Now when I create a new data, I got this error at console.

called

C:\Users\Ryan\Dropbox\Projects\ShopAppBackend\node_modules\sails\node_modules\wa
terline\lib\waterline\model\lib\defaultMethods\save.js:179
      cb(null, data);
      ^
TypeError: object is not a function
    at C:\Users\Ryan\Dropbox\Projects\ShopAppBackend\node_modules\sails\node_mod
ules\waterline\lib\waterline\model\lib\defaultMethods\save.js:179:7
    at bound (C:\Users\Ryan\Dropbox\Projects\ShopAppBackend\node_modules\lodash\
dist\lodash.js:957:21)
    at applyInOriginalCtx (C:\Users\Ryan\Dropbox\Projects\ShopAppBackend\node_mo
dules\sails\node_modules\waterline\lib\waterline\utils\normalize.js:409:80)
    at wrappedCallback (C:\Users\Ryan\Dropbox\Projects\ShopAppBackend\node_modul
es\sails\node_modules\waterline\lib\waterline\utils\normalize.js:308:18)
    at _normalizeCallback.callback.success (C:\Users\Ryan\Dropbox\Projects\ShopA
ppBackend\node_modules\sails\node_modules\waterline\node_modules\node-switchback
\lib\normalize.js:33:26)
    at _switch (C:\Users\Ryan\Dropbox\Projects\ShopAppBackend\node_modules\sails
\node_modules\waterline\node_modules\node-switchback\lib\factory.js:35:28)
    at returnResults (C:\Users\Ryan\Dropbox\Projects\ShopAppBackend\node_modules
\sails\node_modules\waterline\lib\waterline\query\finders\basic.js:166:9)
    at C:\Users\Ryan\Dropbox\Projects\ShopAppBackend\node_modules\sails\node_mod
ules\waterline\lib\waterline\query\finders\basic.js:136:9
    at integrate (C:\Users\Ryan\Dropbox\Projects\ShopAppBackend\node_modules\sai
ls\node_modules\waterline\lib\waterline\query\integrator\index.js:217:10)
    at C:\Users\Ryan\Dropbox\Projects\ShopAppBackend\node_modules\sails\node_mod
ules\waterline\lib\waterline\query\finders\basic.js:87:7
Pewh Gosh
  • 1,031
  • 1
  • 11
  • 29

2 Answers2

0

The problem you're having is due to the async.auto call firing the afterCreate callback() on the first complete iteration inside your _.each.

There are a lot of potential solutions, but whatever solution you use will need an inner wrapping of each iteration. Then some approach to collect each of the iterations and fire the afterCreate's callback() one time.

davepreston
  • 1,260
  • 1
  • 10
  • 20
0

You've got the right idea using async.auto, but you dropped the ball by choosing the synchronous _.each method to wrap it; you want async.each instead:

afterCreate: function( value, callback ) {
    async.each(value.details, function(valueDetail, each_cb) {
        // wrapped
        var getStock = function( cb ) {
          PurchaseInvoiceDetail
            .find({ product: valueDetail.product.id })
            .then(function( detail ) {
              cb(null, SharedInvoiceService.sumNumberArray(_.pluck(detail, 'quantity')));
            });
        };

        var updateStock = ['getStock', function( cb, results ) {
          Product.findOne(valueDetail.product.id).then(function( product ) {
            product.stock = results.getStock;
            product.save(cb);
          });
        }];

        async.auto({
          getStock: getStock,
          updateStock: updateStock
        }, function( err, results ) {
          console.log(results);
          each_cb();
        });
        // end-of-wrapped
  }, 
  // Third argument to async.each is callback for when loop is finished
  callback);

}
sgress454
  • 24,870
  • 4
  • 74
  • 92
  • Aha, thanks for your answer. This is more make sense for me. By the way, I modify my code so I use `if` to make sure `value.details` is not empty (see my updated question). Is my code above right? Or is it an issue from `waterline`? – Pewh Gosh Jun 29 '14 at 14:21
  • Sorry, I wasn't paying close enough attention to your `async.auto` code. You have the function signature backwards for `updateStock`: it should be `(cb, results)`, not the other way around. Also the final callback should be `(err, results)`. Updated my code to reflect the changes. Otherwise looks good (your testing for `value.details` is fine). – sgress454 Jun 29 '14 at 15:44