code4cause
code4cause

Reputation: 129

Javascript async completed callback executing before async functions complete

I've been trying to diagnose this bug for some time now but can't figure out why my completed() function executes before all my asynch functions are done. I'm using the async library:

        async.forEach(data.DBInstances, function (dbInstance, fcallback) {
            let dbtype = dbInstance.Engine;
            let logFilename = log[dbtype].log();
            let instanceId = dbInstance.DBInstanceIdentifier;

            if (tagFilter) {
                let arn = dbInstance.DBInstanceArn;
                checkRDSTag(arn, tagFilter, function (err, found) {
                    if (!err) {
                        //tag was found, continue processing and check other filters...
                        if (found) {
                            if (noFilter || (instanceTypes && instanceTypes.indexOf(dbtype))) {
                                //console.log('db type is: ' + dbtype);
                                processOrCreateLog(instanceId, dbType, function (err, data) {
                                    if (!err) {
                                        console.log("Data: " + JSON.stringify(data));
                                        completed.push(data);
                                        fcallback(null);
                                    } else {
                                        cb(err, null);
                                    }
                                });
                            }
                        } else {
                            //tag wasn't found but was specified, don't process anything...
                            console.log("tag specified was not found on instance: " + instanceId);
                        }
                    } else {
                        console.log("Error checking RDS Tag");
                        cb(err, null);
                    }
                });
            }

            //only process filtered types...
            else if (noFilter || (instanceTypes && instanceTypes.indexOf(dbtype))) {
                console.log('db type is: ' + dbtype);
                processOrCreateLog(instanceId, dbtype, fcallback, function (err, data, fcallback) {
                    if (!err) {
                        console.log("Data: " + JSON.stringify(data));
                        completed.push(data);
                        fcallback(null);
                    } else {
                        cb(err, null);
                    }
                });
            }

        }, testme(completed));

My async functions are running correctly and each completing correctly but my testme(completed) runs immediately before any of my asynch functions ever finish. Not sure why..

my testme(completed) is simply:

function testme(completed) {
    console.log("Completed: " + JSON.stringify(completed));
}

One note, my function to execution on each element itself has asynch functions inside of it (checkRDSTag(), processOrCreateLog(), etc). I'm guessing its something to do with the callback() that async is expecting / tracking executing out of place or something? Not really sure..

Upvotes: 0

Views: 50

Answers (2)

Love-Kesh
Love-Kesh

Reputation: 812

Return callback only when last item iterating:

var index = 0;
async.forEach(data.DBInstances, function (dbInstance, fcallback) {
  let dbtype = dbInstance.Engine;
  let logFilename = log[dbtype].log();
  let instanceId = dbInstance.DBInstanceIdentifier;

  if (tagFilter) {
    let arn = dbInstance.DBInstanceArn;
    checkRDSTag(arn, tagFilter, function (err, found) {
      if (!err) {

         // increment index here
         index++;

         //tag was found, continue processing and check other filters...
         if (found) {
           if (noFilter || (instanceTypes && instanceTypes.indexOf(dbtype))) {
                //console.log('db type is: ' + dbtype);
                processOrCreateLog(instanceId, dbType, function (err, data) {
                  if (!err) {
                    console.log("Data: " + JSON.stringify(data));
                    completed.push(data);

                    //check if last item running
                    if (index===data.DBInstances.length) {
                      return fcallback(null);  
                    } else {
                      fcallback()
                    }
                  } else {
                    cb(err, null);
                  }
                });
              }
            } else {
                  //tag wasn't found but was specified, don't process anything...
                  console.log("tag specified was not found on instance: " + instanceId);
                }
              } else {
                console.log("Error checking RDS Tag");
                cb(err, null);
              }
            });
  }

            //only process filtered types...
            else if (noFilter || (instanceTypes && instanceTypes.indexOf(dbtype))) {
              console.log('db type is: ' + dbtype);
              processOrCreateLog(instanceId, dbtype, fcallback, function (err, data, fcallback) {
                if (!err) {
                  console.log("Data: " + JSON.stringify(data));


                  completed.push(data);

                  //check if last item running
                  if(index===data.DBInstances.length){
                    return fcallback(null);  
                  }else{
                    fcallback()
                  }
                  
                } else {
                  cb(err, null);
                }
              });
            }

          }, testme(completed));

Upvotes: 0

code4cause
code4cause

Reputation: 129

My problem ended up being in my other asynchronous call (processOrCreateLog()) within my iteratee. There was flow control logic in my asynchronous calls that didn't callback so fcallback() never ran.

Also to clarify, async is the async node.js library: https://caolan.github.io/async/docs.html#each

As long as you execute the callback on the iteratee for each element with either an error or null it can track all executions and will then run your final callback properly.

Upvotes: 0

Related Questions