Node JS Async Response

270 views Asked by At

I wrote my 1st application in Node.js with MongoDB and I came across the situation where I need to have 2 db.collection.update statements depending on an IF/ELSE condition. I am trying to return back the operation details in a resJSON JS object but seems like callback is executing before the db.collection.update statements are completed and the default value of resJSON is getting back in response each time.

detail code:

var url = require('url');
var fs = require('fs');
var async = require('async');
var passwordGen = require('../../lib/passwordGen');

var http = require("http");
var server = http.createServer();
server.on('request', request);
server.listen(8080,'127.0.0.1');
function request(request, response) {
    var userData = '';

    request.on('data', function(data) 
    {
        userData += data;
    });
    request.on('end', function() 
    {
      async.auto({                           
        allProcess: function(callback){                                 
            var resJSON  = {'inserted':0,'disabled':0,'error':''};        
            var jsonData = JSON.parse(userData);   
           if(jsonData.length > 0){

            var MongoClient = require('mongodb').MongoClient;

            MongoClient.connect("mongodb://localhost:27017/test_db", function(err,db){
                  if(err) { return console.dir(err); }

                   var collection = db.collection('users');
                   var arrDisableRec = [];  

                   for(i=0;i<jsonData.length;i++){
                      var action = jsonData[i]['action'].toLowerCase();

                      if(action == 'disable'){           
                          arrDisableRec.push(jsonData[i]['email']);                                            
                      }else{
                          var date = new Date();
                          var obj = { 
                                      'createdISO':date,
                                      'created':date,
                                      'updated':date,
                                      'updatedISO':date,
                                      'email':jsonData[i]['email'],
                                      'firstName':jsonData[i]['firstname'],
                                      'lastName':jsonData[i]['lastname'],
                                      'password':passwordGen.getPassword(),
                                      'enabled':true,
                                      'active':true,
                                      'downloaded':true,
                                      'defaultServings':2,
                                      'name':''
                                    };
                                 collection.update(
                                     {'email':jsonData[i]['email']},
                                     {$setOnInsert:obj},
                                     {upsert: true},  
                                     function(err,numberAffected,rawResponse) { 
                                        if(typeof numberAffected.result.upserted != 'undefined'){
                                          resJSON['inserted'] = resJSON['inserted'] + numberAffected.result.upserted.length; 
                                        }   

                                        if(typeof numberAffected.result.nModified != 'undefined'){                                                                    
                                          resJSON['disabled'] = resJSON['disabled'] + parseInt(numberAffected.result.nModified);
                                        }                                                 
                                        if(typeof numberAffected.result.writeError != 'undefined'){                   
                                          resJSON['error'] ='Error Code:'+(numberAffected.result.writeError.code)+', '+numberAffected.result.writeError.errmsg;                                                                                
                                        }
                                        console.log(resJSON); //shows correct values           
                                    }
                          );
                      }     
                      if(arrDisableRec.length > 0){
                          collection.update(
                                     {'email':{$in:arrDisableRec}},
                                     {$set:{'enabled':false}},
                                     {multi:true},
                                     function(err,numberAffected,rawResponse) {  
                                      if(typeof numberAffected.result.upserted != 'undefined'){
                                        resJSON['inserted'] = resJSON['inserted'] + numberAffected.result.upserted.length; 
                                      }   

                                      if(typeof numberAffected.result.nModified != 'undefined'){                                                                    
                                        resJSON['disabled'] = resJSON['disabled'] + parseInt(numberAffected.result.nModified);
                                      }                                                 
                                      if(typeof numberAffected.result.writeError != 'undefined'){                   
                                        resJSON['error'] ='Error Code:'+(numberAffected.result.writeError.code)+', '+numberAffected.result.writeError.errmsg;                                                                                
                                      }     

                                        console.log(resJSON);   //shows correct values    
                                    }
                             );
                        }
                   }          

               });
            } 
            callback(resJSON);
        }         
        },function(resJSON){
            response.writeHead(200,{
                  'Content-Type': 'text/json'
                });                          
            response.end(JSON.stringify(resJSON));    //replies back with default resJSON only.         
        });        
    }); 
 }  

Any suggestions/directions please?

Thanks

2

There are 2 answers

1
Jerome WAGNER On

You have to call the callback at then end of the callback chain.

For example

console.log(resJSON); //shows correct values
callback(null, resJSON);

you will have to make sure that every callback chain end point calls the callback. To handle the for loop you will have to have a mechanism to join all the end points of all the async calls done inside the for loop.

for example

if(err) {
   return callback(err);
}

notice that I used the function callback(err, res) standard node prototype instead of your function callback(res)

better yet, use an async flow library like async or a promise library like bluebird

0
Suman Lama On

This is a counter solution for your issue. It should solve your case for now. Try reading async library. It will be very useful for later.

    var counter = 0;
    function commoncallback(err,numberAffected,rawResponse) { 
        if(typeof numberAffected.result.upserted != 'undefined'){
            resJSON['inserted'] = resJSON['inserted'] + numberAffected.result.upserted.length; 
        }
        if(typeof numberAffected.result.nModified != 'undefined'){                                                                    
            resJSON['disabled'] = resJSON['disabled'] + parseInt(numberAffected.result.nModified);
        }                                                 
        if(typeof numberAffected.result.writeError != 'undefined'){                   
            resJSON['error'] ='Error Code:'+(numberAffected.result.writeError.code)+', '+numberAffected.result.writeError.errmsg;                                                                                
        }
        console.log(resJSON); //shows correct values   
        counter --;
        if(counter == 0) callback(resJSON); //call your main call back here
    }
    for (i=0; i<jsonData.length; i++) {
        if(firstCase) {
            counter ++;
            collection.update({'email':{$in:arrDisableRec}},
                              {$set:{'enabled':false}},
                              {multi:true},commoncallback);
        } else {
              //another update action
              counter++;
        }
    }