node.js async.eachSeries calling final callback too early

11,547

(An answer, not a comment, because the text was too long...)

I'm using async for something too and your question puzzled me. I tried the code below, getting the results even further down. It worked fine for me. (async 0.2.9). Even changing my timeout delays to 0.

The only thing that looked odd was the if(entry.length) block printed console information after the callback. If you were getting a lot of "already exists" messages I could see potentially them coming later - but that doesn't sound like what you are describing.

var async = require('async');  
var console = require('console');
var results = [ ];
results.push( {id:1,data:'a'} );
results.push( {id:2,data:'b'} );
results.push( {id:3,data:'c'} );
results.push( {id:4,data:'d'} );

function doFind( obj, callback ) {
  setTimeout( function() {
    console.log( "finding id=" + obj.data.id );
    obj.data.length = obj.data.id % 2;
    callback( null, obj.data );
  }, 500 );
}

function light( obj ) {
  this.id = obj.id;
  this.data = obj.data;
  this.save = function( callback ) {
    setTimeout( function() {
      console.log( "saving id=" + obj.id );
      callback( null, this );
    }, 500 );
  };
}

var iteration = function(row,callbackDone) {
  doFind({data: row}, function (err,entry) {
    if(entry.length) {
      callbackDone(); 
      return console.log( 'id=' + entry.id + ' already exists');
    }
    var newEntry = new light(row);
    newEntry.save(function (err,doc) {
      console.log( 'id=' + entry.id + ' saved');
      callbackDone();
    });
  });
};

async.eachSeries(results, iteration, function (err) {
  console.log('All done');
});

Results

finding id=1
id=1 already exists
finding id=2
saving id=2
id=2 saved
finding id=3
id=3 already exists
finding id=4
saving id=4
id=4 saved
All done
Share:
11,547
elmalto
Author by

elmalto

SOreadytohelp

Updated on June 04, 2022

Comments

  • elmalto
    elmalto almost 2 years

    This is what I want: I have a large array called results. Each item in that array I want to put in my iteration function and have it saved to my mongoDB (I omitted some steps here as well which are not important). After every single one of those items has been saved to the database (or refused) I want console.log to display 'All done'. Unfortunately All done is displayed almost immediately after calling the series while everything else is still processing. How do I do this right?

    I am using mongoose with a model called 'light' and for readability I omitted all err messages from the code. I use node-async for the eachSeries part

    var async = require('async');    
    var results = [very big array]
    var iteration = function(row,callbackDone) {
      light.find({data: row.data}, function (err,entry) {
        if(entry.length) {
          callbackDone();
          return console.log(entry + ' already exists');
        }
        var newEntry = new light(row);
        newEntry.save(function (err,doc) {
          console.log(entry + ' saved');
          callbackDone();
        });
      });
    };
    
    async.eachSeries(results,iteration, function (err) {
      console.log('All done');
    });
    
  • elmalto
    elmalto over 10 years
    your example works perfectly for me as well, for some reason I couldn't solve my own problem. I switched to a var stream = light.find({}).stream(); solution and halted the stream until the async response came. Probably a bit ugly but it works for me. Thank you very much for your input!