Cache server response (AngularJS, deferred)

268 Views Asked by At

I'm trying to create an angular js service that will fetch data from the server if update needed or return cached array if there is no update. In both cases service should return a promise. Service code:

getLikesForMe = function() {
    var defer = $q.defer(), prom = defer.promise;

    if (angular.isUndefined(this.likesForMe) ||
        updateStatus.likesForMe === true) {
      var that = this;

      prom = $http.get(API_URL + 'likes-to.json')
        .then(function(result){
          updateStatus.likesForMe = false;
          that.likesForMe = result.data;
        });

    } else {
      defer.resolve(this.likesForMe);
    }

    return prom;
  }

Current controller code:

MainUser.getLikesForMe().then(function(result) {
  $scope.likesList = result;
});

Prefered controller code:

$scope.likesList = MainUser.getLikesForMe();

But currently it only works after second function (getLikesForMe()) call, on the first - the list is empty and the "result" variable is undefined.

I'm new to deferred objects and my english is pretty poor, but I hope you understand where the mistake is. Thanks a lot!

2

There are 2 best solutions below

1
Michal Charemza On BEST ANSWER

You have 2 issues

  • On the first call, the promise you return will be resolved with undefined as you're not returning anything from the then success callback from your call to $http.get. If you return that.likesForMe from that callback, I suspect it will work as you expect. You should probably read up on chaining promises. (Shameless plug: I wrote a blog post on AngularJS promises which contains sections on chaining)

  • The code is quite complicated for what it does, and you almost-never have to create a promise via $q.defer() if all you're doing to working with existing promises. It usually makes things more complicated and harder to deal with errors. You can also use $q.when() to create a promise from a non-promise value. So I propose something like

    getLikesForMe = function() {
      var that = this;
      var useCache = !updateStatus.likesForMe && !angular.isUndefined(this.likesForMe);
    
      return useCache ? $q.when(that.likesForMe) : $http({
        method: 'GET',
        url: API_URL + 'likes-to.json'
       }).then(function(results) {
         updateStatus.likesForMe = false;
         that.likesForMe = results.data;
         return that.likesForMe;
       });
    });
    

You could also read up in the docs for $http cache to see if you could come up with a solution that uses it.

0
spacemigas On

If you prefer the later version of the controller code then you can't return a promise from the service.

You could return an empty object that would be augmented when you got back from the http call.

You'd then have to issue a $rootScope.$apply() so your controller is notified.