1

I have controller and service with cached value.

app.service('nameService', ['$q','$http', function($q, $http){
    var that = this;

    that.name = null;

    this.index = function(){
        var deferred = $q.defer();

        if (that.name !== null) {

            // fired cahed value
            console.log('cashed');

            deferred.resolve(that.name);

        } else {
            $http.get('/api/name').success(
                function(response) {

                    // fired request to server
                    console.log('server');

                    that.name = response.name;
                    deferred.resolve(that.name);

                }
            );
        }

        return deferred.promise;
}]);

When requests in controller send with interval like

app.controller('nameCtr', ['$scope', '$timeout', 'nameService'], 
                function($scope, $timeout, nameService){

    nameService.index(); // console.log server
  
    $timeout(function(){
    
        nameService.index(); // console.log cached
      
    },3000)
    
    $timeout(function(){
    
        nameService.index(); // console.log cashed
      
    },6000)
  
}]);

It's all ok, first request fired server, other fired cached. But if I send request without delay I send multiple requests to server. like:

...
nameService.index(); // console.log server
nameService.index(); // console.log server
nameService.index(); // console.log server
...

They all fired server, but I need cached. I have multiple controllers with the same service. What the best way to send only one request to server?

colmer
  • 99
  • 6
  • 1
    Not a duplicate, as this is about caching the http response. The correct way would be to pass `{cache: true}` and not cache the promise anyway. – Benjamin Gruenbaum Dec 30 '15 at 13:26

2 Answers2

1

How to prevent the same requests to server?

Well, you can change $http.get('/api/name') to $http.get('/api/name', {cache: true}), this is actually built into Angular and works just fine.

This brings the interesting question of how to do it without using Angular's help for a generic promise - just as easy:

this._data = null;
this.index = function() { 
    return this._data || (this._data = $http.get('/api/name'));
}

That way, if it's cached it'll return the left hand side and if it's not it'll cache the right hand side.

The important part is that we cached the promise and not the data. That's the "trick" you've been missing.

Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
0

You can do it in next way

app.service('nameService', ['$q','$http', function($q, $http){
    var that = this;

    that.nameDeffered = null;

    this.index = function(){
        if (that.nameDeffered === null) {
            that.nameDeffered = $q.defer();
            $http.get('/api/name').success(
                function(response) {
                    // fired request to server
                    console.log('server');                        
                    that.nameDeffered.resolve(response.name);
                }
            );
        }

        return that.nameDeffered.promise;
    }
}]);
YuriyP
  • 4,210
  • 4
  • 25
  • 35
  • 1
    stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it – Benjamin Gruenbaum Dec 29 '15 at 17:53
  • If author need to cache all respone which return $http.get('/api/name') we can write something like that in if statement that.nameDeffered = $http.get('/api/name'); But he want to cache only response.name. Thats why I created new deffered. – YuriyP Dec 30 '15 at 13:20
  • That's totally not needed - look at my answer for how it can be accomplished without it. – Benjamin Gruenbaum Dec 30 '15 at 13:25
  • Yes, I looked at your answer, but your code do not do what original author code do. So if you use your service: nameService.index().success(function(result){console.log(result);}); you will see in console something like this: { name: 'bob', age: 24, email: 'example@example.com' }. But if you use author method you will see in console 'Bob'. This is the difference. – YuriyP Dec 30 '15 at 13:33
  • All it takes to do it would be to do `.then(x => x.name)` that's all. – Benjamin Gruenbaum Dec 30 '15 at 13:35
  • Sure. But in some cases it may be crusial to return not all data for sake of incapsulation. – YuriyP Dec 30 '15 at 13:37
  • 1
    So `.then(x => x.name)` can be added to the code in my answer... creating a deferred is just misunderstanding promises. Your code for example - does not handle server errors correctly. – Benjamin Gruenbaum Dec 30 '15 at 13:38