async service initialization

2,872 views
Skip to first unread message

Elad Benedict

unread,
Nov 29, 2013, 4:23:46 PM11/29/13
to ang...@googlegroups.com
Hi,
 
I understood that Angular can handle async service initialization (via $routeProvider.resolve) so that my controller is only created once the service it depends on is initialized. This solves Controller => Service dependencies.
What about service dependencies though (i.e. ServiceA => ServiceB)?
Suppose I have serviceA which depends on serviceB and serviceB needs to do some async operation in order to initialize. How can I get it to complete its initialization prior to its injection into serviceA?
 
Thanks,
Elad.

Daniel Tabuenca

unread,
Nov 29, 2013, 5:15:58 PM11/29/13
to ang...@googlegroups.com

There is really no concept of “service initialization” in angular. A service is just a singleton object returned by a factory function you registered in a module. For example:

myModule.factory("myService", function(){
   return  "THIS IS MY SERVICE";
});

There is no concept of initialization. Whenever you inject this service it simply calls the factory function ONCE and caches the return so all subsequent injections will use the same object:

function myController($scope, myService){
    console.log(byService); // Will log "THIS IS MY SERVICE"
}

The resolve functionality of $routeProvider does not really know about services or have any concept of “service initialization”. It just works with promises and these promises can come from anywhere.

What you are referring to is likely the very common practice of having service methods return promises, and using resolve to postpone controller initialization until that promise is resolved. For example:

myModule.factory("userService", function(){
   return  {
     getUsers: function(){
       return $http.get('/users').then(function(response){
         return response.data;
       });
     }
   };
});

And then in your resolve:

resolve: {
    users: function(userService){
        return userService.getUsers() //returns a promise
    }
}

If in order for the user service to return the users it depends on some other service method which is also async, there is nothing stopping it from waiting on the other services promise before resolving it’s own. For example:

myModule.factory("userService", function(someOtherService){
   return  {
     getUsers: function(){

       var otherServicePromise = someOtherService.doSomething();

       return otherServicePromise.then( function(){
          return $http.get('/users').then(function(response){

            return response.data;
          });
       );
     }
   };
});

This is called “promise chaining” and is a very common and elegant solution to handling async things properly. The service will first call someOtherService.doSomething() which returns a promise. When that is resolved it will do the $http.get() to get the users. This will eventually resolve the promise with the response.data.

Because we are returning the last promise in the chain, our resolve will not conclude until every single promise in the chain has resolved, so this should handle any sort of dependency structure.


Elad Benedict

unread,
Nov 30, 2013, 1:17:59 AM11/30/13
to ang...@googlegroups.com
Thank you for your detailed reply!
 
Although promise chaining indeed solves the problem is requires my service's API to be async, which, in my case it really shouldn't be.
In my application I have state that is persisted to the indexDB and I have services whose API depends on that state.
As a result, when I load my application I'd like to be able to "initialize" my services with their previous state (taken from the indexDB), which is an async opertaion.
 
What I'd like is to have a phase in which I initialize serviceB, loading its state and having it fully functional again, and only then allow serviceA to be able to use its API (after having loaded its state from persistence, etc.)
Once all the state is loaded and the services are "initialized", only then should controllers access them - at which point I'd like the API to be used in a synchronous manner so writing code would be less cumbersome.
 
The key point here is that I want to avoid having to expose async APIs all around due to a single async operation required only at bootstrap.
 
Thanks again,
Elad.

Mauro Servienti

unread,
Nov 30, 2013, 1:37:46 AM11/30/13
to ang...@googlegroups.com
Put your services in a dedicated module and hook the run event, make the run function depend on your services and initialize everything you need here.

.m

Sent from my Amazing Yellow Lumia, typos are guaranteed ;-)

From: Elad Benedict
Sent: ‎30/‎11/‎2013 07.18
To: ang...@googlegroups.com
Subject: [AngularJS] Re: async service initialization

--
You received this message because you are subscribed to the Google Groups "AngularJS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to angular+u...@googlegroups.com.
To post to this group, send email to ang...@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/groups/opt_out.

Daniel Tabuenca

unread,
Nov 30, 2013, 3:15:49 AM11/30/13
to ang...@googlegroups.com

I see…. One thing you can do is have an “initialized” promise on each service:

app.service('serviceA', function($q, serviceB){
      var initDefer = $q.defer();
      this.initialized = initDefer.promise;

      serviceB.initialized.then(function(){
        doSomeServiceAsyncInit().then(function(){
           initDefer.resolve();

         });
       });

}

app.service('serviceB', function($q){
      var initDefer = $q.defer();
      this.initialized = initDefer.promise;

       doSomeAsyncInit().then(function(){
          initDefer.resolve();

       });
}

then in your route provider:

   resolve: {
       initServices: function(serviceA){ return serviceA.initialized  }
    }

Does this make sense?

Tobias Gesellchen

unread,
Dec 2, 2013, 11:13:52 AM12/2/13
to ang...@googlegroups.com
Hi,

we had similar issues and solved them like you described with dedicated initialization promises for some services. We extraced the relevant code into an own service and made replaced the specific promises inside our services with a more or less declarative configuration. You can find more details at https://groups.google.com/forum/#!topic/angular/6CxwIL4QSfY/discussion or http://blog-it.hypoport.de/2013/10/28/angularjs-ordered-init-async-promise/ and we would also like to get some feedback. Your solution looks quite similar to ours (apart from using the route provider), so we'd be glad to talk about a better generalization and probably of native application initialization support by Angular.

Tobias.
Reply all
Reply to author
Forward
0 new messages