Search code examples
javascriptjquerydeferred

jQuery Deferred not working as expected


This is my first time using $.Deferred(); and it's proving to be a mission.

I have read jQuery Deferred not working, but that applies to ajax events, so doesn't really help in my case.

I have the following code in javascript:

var deferred = $.Deferred();

deferred.done(function() {
    console.log(auth.loggedIn);
    if(auth.loggedIn) {
        app.page = 'usr/dashboard';
    }
}

deferred.resolve(auth.initialize());

My auth object looks like this:

var auth = {

    loggedIn:false,

    initialize:function() {
        var data = {
            'login_token':app.getCookie('login_token'),
            'usr_id':app.getCookie('usr_id'),
            'source':config.source,
            'language_code':config.language_code,
            'country_code':config.country_code
        }
        app.api('usr/loggedin',data,function(data) {
            var response = JSON.parse(data);
            if(!response.result) {
                auth.loggedIn = false;
            }
            auth.loggedIn = true;
            console.log(auth.loggedIn);
        },'post');
    }
}

Now, if I console.log(response) in the app.api callback, the response is exactly what I expect it to be, if I console.log(auth.loggedIn) I get true.

If I console.log in deferred.done and console.log(auth.loggedIn) (in initialize I get two different values. What am I doing wrong and how can I make my app wait for auth.loggedIn to be set?

UPDATE

This is how I am now doing it in my main file:

auth.initialize().done(function(auth) {
    if(auth.loggedIn) {
        app.page = 'usr/dashboard';
    }
    jsRouter.initialize();
});

jsRouter is based on this 20 line javascript router and now only sometimes shows the template page, regardless of the page I am on.

The jsRouter.initialize function looks like this:

initialize:function() {
    window.addEventListener('hashchange',jsRouter.router);
    window.addEventListener('load',jsRouter.router);

    /* ERROR PAGES */
    jsRouter.route('404','errors/404',function() {});
    jsRouter.route('500','errors/500',function() {});

    /* ACTUAL PAGES */
    jsRouter.route('/','home',function() {});
    jsRouter.route('/home','home',function() {});
    jsRouter.route('/usr/register','usr/register',function() {});
    jsRouter.route('/usr/login','usr/login',function() {});
    jsRouter.route('/usr/forgotpassword','usr/forgotpassword',function() {});
    jsRouter.route('/usr/activate','usr/activate',function() {});
    jsRouter.route('/usr/dashboard','usr/dashboard',function() {});
    jsRouter.route('/adr','adr/index',function() {});
},

I have updated my app.api callback to look exactly like what Felix King suggested.


Solution

  • You are currently resolving the deferred object with undefined, which does not make sense. You are also resolving it before the app.api callback was executed. The deferred object has to be resolved by the asynchronous callback.

    auth.initialize should return the deferred object / promise, and you have to resolve it in the app.api callback:

    var auth = {
    
        loggedIn:false,
    
        initialize:function() {
           var deferred = new $.Deferred();          // <- create deferred object
            var data = {
                'login_token':app.getCookie('login_token'),
                'usr_id':app.getCookie('usr_id'),
                'source':config.source,
                'language_code':config.language_code,
                'country_code':config.country_code
            }
            app.api('usr/loggedin',data,function(data) {
                auth.loggedIn = JSON.parse(data).result;
                deferred.resolve(auth);              // <- resolve when response was received
            },'post');
            return deferred.promise();               // <- return promise
        }
    }
    
    auth.initilize().done(function(auth) { 
      // at this point the login succeeded (or failed)
    });