3

I am using async each to loop through and constructing an object called coupon_bo. Surprisingly inside processbo function, I am seeing a side effect where only the last copy of coupon_bo object is available to processbo function.

My understanding is that since coupon_bo is local to each iteration, there should be a new object for iteration.

Am I missing something?

function hitApplyLogic(coupon_names, coupons_list, req, callback) {
    async.each(coupon_names, function(coupon_name, callback) {
        var coupon_bo = new coupon_objects.CouponsBO();
        coupon_bo.incoming_request = req.body;
        coupon_bo.incoming_request['coupon_code'] = coupon_name.cn;
        coupon_bo.incoming_request['list_offers'] = true;

        setTimeout(function()
        {
            console.log("CONSOLE-BO: " + JSON.stringify(coupon_bo));

        }, 1000);
    });
}
Madhur Ahuja
  • 22,211
  • 14
  • 71
  • 124
  • please put some more code, like where are you calling callback and all – suraj.tripathi Oct 01 '16 at 17:17
  • async.each() run asynchronously and may corrupt your data `coupon_bo`. You may like to use `async.eachSeries()` or use `this.coupon_bo` rather than `var coupon_bo` – Arif Khan Oct 01 '16 at 17:17
  • @suraj99934 callback is called inside the function(result) {} – Madhur Ahuja Oct 01 '16 at 17:27
  • can you replace this `async.each(coupon_names, function(coupon_name, callback) {` with `async.eachLimit(coupon_names, 1, function(coupon_name, callback) {` and check problem is still occuring? – suraj.tripathi Oct 01 '16 at 17:30
  • @suraj99934 see the updated code. It reproduces the problem which is happening. Always last BO is printed in console.log. I guess that's behaviour of each, it doesn't create a new scope – Madhur Ahuja Oct 01 '16 at 17:35
  • no that is not true, check this code `var async = require('async'); var coupon_names = [1, 2, 3, 4]; async.each(coupon_names, function(coupon_name, callback) { var coupon_bo = {}; coupon_bo[Math.random().toString()] = "hi"; setTimeout(function() { //callback is called here console.log(coupon_bo); callback() }, 500); });` – suraj.tripathi Oct 01 '16 at 17:42
  • It will always crete a new variable `coupon_bo` – suraj.tripathi Oct 01 '16 at 17:45
  • What version of async you are using, can you try on 1.4 @suraj99934 – Madhur Ahuja Oct 01 '16 at 19:06
  • checked with 1.4.2 also, all the `coupon_bo` objects are different, here is the output of my program `{ '0.35520064564570686': 'hi' } { '0.926551623613157': 'hi' } { '0.45901556838783253': 'hi' } { '0.13620334446253368': 'hi' }` – suraj.tripathi Oct 01 '16 at 19:20

2 Answers2

1

async.each has no guarantee of running the tasks in order.

Per the documentation:

Note, that since this function applies iteratee to each item in parallel, there is no guarantee that the iteratee functions will complete in order.

I'm not sure what you meant by processbo function. But var coupon_bo should be unique to each instance of the iteratee that is ran. So there should be no issue of it being overwritten by the other ones.

I'm also not sure why you're using setTimeout to log coupon_bo after 1s.

I did find something missing in your implementation which is the call to the callback function in the iteratee async.each(coupon_names, function(coupon_name, callback) {

Without calling it you'll be forever stuck at async.each

function hitApplyLogic(coupon_names, coupons_list, req, callback) {
    async.each(coupon_names, function(coupon_name, eachCallback) { //Changed callback to eachCallback to avoid confusion with the one received in hitApplyLogic
        var coupon_bo = new coupon_objects.CouponsBO();
        coupon_bo.incoming_request = req.body;
        coupon_bo.incoming_request['coupon_code'] = coupon_name.cn;
        coupon_bo.incoming_request['list_offers'] = true;

        setTimeout(function()
        {
            console.log("CONSOLE-BO: " + JSON.stringify(coupon_bo));
            eachCallback(null); // Finished doing all the work with this particular coupon_name
        }, 1000);
    },
    , function(err) { //This function is called once all the coupon_names were processed

        if(err) {
          // One of the coupon_names returned an error
          console.log('One of the coupon_names returned an error');
          return callback(err); // Callback received in hitApplyLogic
        } else {
          // Everything went OK!
          console.log('All coupons were constructed');
          return callback(null); // Callback received in hitApplyLogic
        });
}
fmello
  • 563
  • 2
  • 9
0

Here is the Solution for your problem, Async's each immediately prints out all elements

async.eachSeries() will iterate array item one at a time, where async.each() will iterate all the items at once in parallel.

Community
  • 1
  • 1
Raviraj Subramanian
  • 364
  • 1
  • 6
  • 11