kiran Gopal kiran Gopal - 1 month ago 5
Node.js Question

async giving callback already called error?

I am trying to achieve the following scenario. But while executing I am getting an error Uncaught Error: Callback was already called.

Can anyone find what I am doing wrong ?

async.forEachOf(code,function(item, index, done){
var currentItemId = item.id;
var subitemCount = item.subitem;
for(var k=0; k< subitemCount ; k++){
$.ajax({
url: 'api/item' + k,
success: function(result) {
code["allSub"].push(result);
done();
},
error: function(err) {
done(err);
}
})
}
},function(){
console.log(code);
});

Answer

The issue lies in the for(var k=0; k< subitemCount ; k++){ declaration.

Notice that for each item.subitem you are making one ajax call. And for after each of those calls return, you are calling the done() callback. Therefore the code is actually calling the callback (i.e. done()) multiple times. Once for each ajax call and thus once for each item.subitem.

That's why you're having the Uncaught Error: Callback was already called.

In order to avoid this, you'd need to make another async.forEachOf instead of the

Try using this:

async.forEachOf(code,function(item, index, doneItem){
    var currentItemId = item.id;    
    var subitemCount = item.subitem;
    async.forEachOf(subitemCount, function (subItem, subItemIndex, subItemDone){
        $.ajax({
            url: 'api/item' + subItemIndex,
            success: function(result) {              
                code["allSub"].push(result);
                subItemDone();
            },
            error: function(err) {                
                subItemDone(err);             
            }
        })
    }, function(err) {
        //Called once all the current item's subItems ajax calls were finished 
        doneItem(err);
    }


},function(){
    //Called once all items in "code" finished their own subItem ajax calls
    console.log(code);
});

If item.subitem is a numeric value instead of an array, you can try using the async.times

async.forEachOf(code,function(item, index, doneItem){
    var currentItemId = item.id;    
    var subitemCount = item.subitem;
    async.times(subitemCount, function (subItemIndex, subItemDone){
        $.ajax({
            url: 'api/item' + subItemIndex,
            success: function(result) {     
                code["allSub"].push(results);         
                subItemDone();
            },
            error: function(err) {                
                subItemDone(err);             
            }
        })
    }, function(err, results) {
        //Called once all subItem ajax calls were finished
        doneItem(err);
    }


},function(){
    //Called once all items in "code" finished their own subItem ajax calls
    console.log(code);
});