Use Promise and service together in Angular

2019-03-14 13:37发布

问题:

My question is based on this topic in Angular Google group.
I want to provide a service which stores some basic data retrieved from the backend via $http, then I only need to fetch those data once. like,

var load = function() {
   return $http.get(...).then(function(data) {
       return data.user; 
   });
};

module.factory("userProvider", function() {
    var user;
    var getUser = function() {
        if(!user) {
            load().then(function(data) {
               user = data;
           });
        }
        return user;
    };
    return {
        getUser : getUser
    }
});

module.controller("UserController", ["userProvider", function UserController("userProvider") {
    var user = userProvider.getUser();
    // do something with user
}]);

The problem is that the promise chain ends in userProvider but not in controller, so the user is undefined the first time I use this controller since the data has not been returned.

How can I use such a storage service and return the data correctly? Thanks!

回答1:

You can just create your own promise. Here is the modified code:

module.factory( "userProvider", function( $q ) {
  // A place to hold the user so we only need fetch it once.
  var user;

  function getUser() {
    // If we've already cached it, return that one.
    // But return a promise version so it's consistent across invocations
    if ( angular.isDefined( user ) ) return $q.when( user );

    // Otherwise, let's get it the first time and save it for later.
    return whateverFunctionGetsTheUserTheFirstTime()
    .then( function( data ) {
      user = data;
      return user;
    });
  };

  // The public API
  return {
    getUser: getUser()
  };
});

Update: The solution below by @yohairosen is a great one for many circumstances, but not for all. In some circumstances, we would only want to cache the successful result, as I have done here. If, for example, a failure of this request indicates the user needs to log in first, we would not want the next call (presumably after logging in) to deliver the cached failure. In cases where the method isn't necessarily consistent from call-to-call in all circumstances, this method is better; in all other cases, @yohairosen's solution is simpler and recommended.



回答2:

It's a bit of an overhead to create your own promise, angular's $http creates one for you anyway. What you're looking for is caching and http can handle it for you by passing cache:true to the service call.

So you can simply do something like this:

 module.factory("userProvider", function() {
   var getUser = function() {
   return $http.get(..., {cache:true}).then(function(data) {
       return data.user; 
   });

   return {
       getUser : getUser
   }
});