Rodrigo Pereira
Rodrigo Pereira

Reputation: 1914

How use async code inside app.run?

I have a run block that is querying my server to check if the user is authenticated.

.run(function($http, userService){
    var base_url = 'http://server.com:3000';

    $http.get(base_url + '/users/isloggedin')
        .success(function(data, status, headers, config){
            userService.setUserData(data.userData);
            userService.setIsUserLoggedIn(true);
        });
})

Later, I have another run that will require info from the first run block. The problem with this is that my run code has async code and I am not getting the true value at the first time to userService.getIsUserLoggedIn().

How can I tell angularjs to execute the second run block only after the first one has been completed?

The second run block:

.run(function($rootScope, $location, $state, userService){
    //Run to handle authentication

    var authOnly = ['/painel'];
    var unAuthOnly = ['/home'];

    var checkAuthRoute = function(url){
        var exist = authOnly.indexOf(url);
        return exist > -1;
    };

    var checkUnAuthRoute = function(url){
        var exist = unAuthOnly.indexOf(url);
        return exist > -1;
    };

    $rootScope.$on('$stateChangeStart', function(evt, toState, toParams, fromState, fromParams){
        console.log(toState.url + ' ' + fromState.url + ' - ' + userService.getIsUserLoggedIn());

        if(!userService.getIsUserLoggedIn() && checkAuthRoute(toState.url)){
            console.log('Aqui..');
            evt.preventDefault();
            $state.go('login');
        }

    });
})

Thanks

Upvotes: 1

Views: 57

Answers (2)

antimatter
antimatter

Reputation: 3480

You can use callbacks to make chain async requests in Javascript. Something like this might work:

.run(function($http, $rootScope, $location, $state, userService){
    var base_url = 'http://server.com:3000';

    $http.get(base_url + '/users/isloggedin')
        .success(function(data, status, headers, config){
            userService.setUserData(data.userData);
            userService.setIsUserLoggedIn(true);
            handleAuth($rootScope, $location, $state, userService);
        });
})

And define this function before the .run code from above:

function handleAuth($rootScope, $location, $state, userService){
    var authOnly = ['/painel'];
    var unAuthOnly = ['/home'];

    var checkAuthRoute = function(url){
        var exist = authOnly.indexOf(url);
        return exist > -1;
    };

    var checkUnAuthRoute = function(url){
        var exist = unAuthOnly.indexOf(url);
        return exist > -1;
    };

    $rootScope.$on('$stateChangeStart', function(evt, toState, toParams, fromState, fromParams){
        console.log(toState.url + ' ' + fromState.url + ' - ' + userService.getIsUserLoggedIn());

        if(!userService.getIsUserLoggedIn() && checkAuthRoute(toState.url)){
            console.log('Aqui..');
            evt.preventDefault();
            $state.go('login');
        }

    });
}

Or a much more popular alternative (to prevent constant chaining of callbacks which makes code unreadable--aka the Pyramid of Doom), is to use Promises.

A promise takes an async function and returns a promise, which you can use to chain requests (ex. the $http method returns a promise that you're using called success). It is not available in ECMAScript 5, but will be in 6. People have made a bunch of implementations of Promises, such as Kris Kowal's Q, and Angular has a stripped down version of this library called $q.

Upvotes: 1

Rasalom
Rasalom

Reputation: 3111

Because this is async call, technically first block is finished, but the call(s) inside it wasn't. The only thing that comes to my mind is adding promise into userService and communicate through it:

.run(function($http, $q, userService){
    var base_url = 'http://server.com:3000';
    var deferred = q.defer();
    $http.get(base_url + '/users/isloggedin')
        .success(function(data, status, headers, config){
            userService.setUserData(data.userData);
            userService.setIsUserLoggedIn(true);
            deferred.resolve();
        });
    userService.setPromise(deferred.promise);
})

And in second run:

.run(function(userService){
        userService.getPromise().then(function(){
           //code that requires first run to finish
        });
    })

But if somewhere you need code from second run to finish(I mean do smth only after second run), it will require same structure again, and that's not really good, so you need to change the logic.

Upvotes: 0

Related Questions