Avoid multiple ajax requests angularJS

All we need is an easy explanation of the problem, so here it is.

I am trying to avoid multiple ajax requests to the server in a factory. I already added a small caching service, but it is not enough for what I aim: this factory can be called several times before the server responds, causing the generation of multiple requests to the server.

To avoid this I added a second promise object, which if the AJAX request have been performed and the object is not yet in cache, than it should wait for a second promise to be resolved, but looks like I am missing something.

This is my code:

myApp.factory('User', ['Restangular', '$q',
  function (Restangular, $q) {
    var userCache, alreadyRun = false;
    return {
      getUser: function () {
        var deferred = $q.defer(), firstRun= $q.defer();

        if (!userCache && !alreadyRun) {
          alreadyRun = true;

          Restangular.all('user').getList().then(function (user) {
            console.log('getting user live ');
            userCache = user[0].email;
            firstRun.resolve(user[0].email);
          });
        } else if (!userCache && alreadyRun) {
          console.log('waiting for the first promise to be resolved ');
          firstRun.then(function(user) {
            console.log('resolving the promise');
            deferred.resolve(userCache);
          });

        } else {
          console.log('resolving the promise from the cache');
          deferred.resolve(userCache)
        }
        return deferred.promise;
      }
    };
  }
]);

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

You could just return the original promise if the request has already been made. Something like this should work;

myApp.factory('User', ['Restangular', '$q',
  function (Restangular, $q) {
    var deferred = false;

    return {
      getUser: function () {

        if(deferred) {
          return deferred.promise;
        }

        deferred = $q.defer();

        Restangular.all('user').getList().then(function (user) {
          deferred.resolve(user[0].email);
        });

        return deferred.promise;
      }
    };
  }
]);

Also have a look at the Restangular documentation for caching requests

Method 2

Everytime you run getUser, a new defer is created for firstRun. If it’s already ran, you call firstRun.then, but that promise is never resolved.

Method 3

Thanks all for the answers, in the meanwhile I found a way to cache that particular factory:

.factory('User', ['Restangular', '$q',
  function (Restangular, $q) {
    var userCache, promises = [];
    return {

      getUser: function () {

        var deferred = $q.defer();

        if (promises.length > 0) {

          promises.push(deferred);

        } else if (!userCache) {

          promises.push(deferred);

          Restangular.all('user').getList().then(function (user) {
            var i;
            userCache = user[0];
            for (i = promises.length; i--;) {
              promises.shift().resolve(userCache);
            }
          });

        } else {

          deferred.resolve(userCache);

        }

        return deferred.promise;

      }
    };
  }
]);

Basically the idea is to create an array of promises while userCache is not ready, then resolve the whole queue once the request is ready and finally directly resolve the promise with the cached value for each future request.

I described the implementation of this promise caching here.

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply