Search code examples
node.jsasynchronouspromiseqjquery-callback

How to use q.nfcall if .then part is conditional


I used Q library to server side (nodejs) to avoid the callback pyramid.

The callback pyramid is as below :

var isNext = <any boolean>;
db.model1.find({a: val}).exec().
then(function(err, res){
if( res && isNext){
  db.model2.find({b: val}).exec().then(function(err, res){ 
      callback();
  });
}else{
  callback();
}
});

I used q library to avoid the callback pyramid like this :

var isNext = <any boolean>;
q.nfcall(find1(id))
    .then( function( MODEL1 ){
        var deferred = q.defer();
        if( MODEL1 && isNext){
            deferred.promise = find2( id );
        }else{
            deferred.resolve(MODEL1);
        }
        return deferred.promise;
    })
    .fail(function(err){
        console.log('--> err : ' + JSON.stringify(err));
    })
    .done(function(){
        console.log('update done..');
       // main callback
    });

and find1 and find2 functions are as :

function find1(id){
var deferred = q.defer();
db.model1.findOne({_id: id}).exec()
    .then(function(model){
        if( model ){
            // some operation
            deferred.resolve( model );
        }else{
            deferred.reject( 'error' );
        }
    },function(err){
        deferred.reject( err );
    }
);
return deferred.promise;
}

function find2(id){
var deferred = q.defer();
db.model2.findOne({_id: id}).exec()
    .then(function(model){
        if( model ){
            // some operation
            deferred.resolve( model );
        }else{
            deferred.reject( 'error' );
        }
    },function(err){
        deferred.reject( err );
    }
);
return deferred.promise;
}

So, the problem with this is, the .then part, when Q library is used, is not getting executed . In this problem, the call is always going into the .fail part of the execution. What I am expecting is the execution should go inside the .then part when find1() call is executed.


Solution

  • Your find1 function already is returning a promise - you should not must not use it with Q.nfcall.

    Just do

    var isNext = <any boolean>;
    find1(id)
    .then(function(MODEL1) {
        if (MODEL1 && isNext) {
            return find2(id);
        } else {
            return MODEL1; // you can just return plain values as well, btw
        }
    })
    .fail(function(err) {
        console.log('--> err : ' + JSON.stringify(err));
    })
    .done(function() {
        console.log('update done..');
        // main callback
    });
    

    Notice that you should avoid the deferred antipattern. Those exec invocations already return promises, so all you might want to do is cast them to Q promises and reject on falsy results.

    function find1(id) {
        return q(db.model1.findOne({_id: id}).exec())
        .then(function(model) {
            if (model) {
                // some operation
                return model;
            } else {
                throw new Error('error');
            }
        });
    }
    function find2(id) {
        // analogous
    }