Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Caching a promise object in AngularJS service

I want to implement a dynamic loading of a static resource in AngularJS using Promises. The problem: I have couple components on page which might (or not, depends which are displayed, thus dynamic) need to get a static resource from the server. Once loaded, it can be cached for the whole application life.

I have implemented this mechanism, but I'm new to Angular and Promises, and I want to make sure if this is a right solution \ approach.

var data = null;
var deferredLoadData = null;

function loadDataPromise() {
  if (deferredLoadData !== null)
    return deferredLoadData.promise;

  deferredLoadData = $q.defer();

  $http.get("data.json").then(function (res) {
    data = res.data;
    return deferredLoadData.resolve();
  }, function (res) {
    return deferredLoadData.reject();
  });

  return deferredLoadData.promise;
}

So, only one request is made, and all next calls to loadDataPromise() get back the first made promise. It seems to work for request that in the progress or one that already finished some time ago.

But is it a good solution to cache Promises?

like image 572
andrew.fox Avatar asked Sep 11 '13 15:09

andrew.fox


People also ask

What is promise in AngularJS?

Promises in AngularJS are provided by the built-in $q service. They provide a way to execute asynchronous functions in series by registering them with a promise object. {info} Promises have made their way into native JavaScript as part of the ES6 specification.

What is defer promise in AngularJS?

defer() to create a Promise. A Promise is a function that returns a single value or error in the future. So whenever you have some asynchronous process that should return a value or an error, you can use $q. defer() to create a new Promise.

What is resolve in promise angular?

resolve() The Promise. resolve() method "resolves" a given value to a Promise . If the value is a promise, that promise is returned; if the value is a thenable, Promise. resolve() will call the then() method with two callbacks it prepared; otherwise the returned promise will be fulfilled with the value.

What is promise and then in angular?

Promises can be executed by calling the then() and catch() methods. The then() method takes two callback functions as parameters and is invoked when a promise is either resolved or rejected. The catch() method takes one callback function and is invoked when an error occurs.


2 Answers

Is this the right approach?

Yes. The use of memoisation on functions that return promises a common technique to avoid the repeated execution of asynchronous (and usually expensive) tasks. The promise makes the caching easy because one does not need to distinguish between ongoing and finished operations, they're both represented as (the same) promise for the result value.

Is this the right solution?

No. That global data variable and the resolution with undefined is not how promises are intended to work. Instead, fulfill the promise with the result data! It also makes coding a lot easier:

var dataPromise = null;  function getData() {     if (dataPromise == null)         dataPromise = $http.get("data.json").then(function (res) {            return res.data;         });     return dataPromise; } 

Then, instead of loadDataPromise().then(function() { /* use global */ data }) it is simply getData().then(function(data) { … }).

To further improve the pattern, you might want to hide dataPromise in a closure scope, and notice that you will need a lookup for different promises when getData takes a parameter (like the url).

like image 120
Bergi Avatar answered Oct 09 '22 01:10

Bergi


For this task I created service called defer-cache-service which removes all this boiler plate code. It writted in Typescript, but you can grab compiled js file. Github source code.

Example:

function loadCached() {
   return deferCacheService.getDeferred('cacke.key1', function () {
      return $http.get("data.json");
   }); 
} 

and consume

loadCached().then(function(data) {
//...
});

One important thing to notice that if let's say two or more parts calling the the same loadDataPromise and at the same time, you must add this check

if (defer && defer.promise.$$state.status === 0) {
   return defer.promise;
}

otherwise you will be doing duplicate calls to backend.

like image 36
Andzej Maciusovic Avatar answered Oct 09 '22 01:10

Andzej Maciusovic