Robert Koritnik
Robert Koritnik

Reputation: 105029

Get data synchronously when cached or get it async when unavailable

I have an AngularJS app that uses routing and views. When a particular view is loaded and controller instantiates I have to prepare some $scope model data. This data can be provided by a dependent service or when service doesn't have it, I make an async call to get it from the server.

When I finally do have this data I have to change it a bit and then put it on my $scope.

This I think perfectly falls into deferred/promise API. Getting data from the service is done using $resource service instance and is a promise already. The only problem I'm having is converting my synchronous code to a deferred/promise pattern.

Question

How can I change my synchronous code processing to become async so my function that provides data always returns a promise which would be immediately resolved when using sync code and after a while when asynchronously calling my server?

So process:

  1. try getting data synchronously
  2. if sync failed, get it async
  3. success/fail
    1. data available => manipulate it
    2. data unavailable (either way) => reset state

What I tried

var getData = function() {
    var defer = $q.defer();

    defer.promise
        .then(function () {
            // return cached item
            return dataCacheService.get("dataKey");
        })
        .then(function(data) {
            // cache returned data?
            if (!data)
            {
                // no? get it from server returning a promise
                return dataResource.get({
                    set: "models",
                    id: $routeParams.id
                });
            }
        })
        .then(function (data) {
            // server returned data?
            if (!!data) // <= PROBLEM!!! This is not null, but is a Resource with unresolved promise?
            {
                // yes? fine. manipulate it
                delete data.details;
                delete data.type.description;

                $scope.lists.set(data.type);

                return data;
            }
            // no data. sorry...
            $scope.resetType();
        })
        // something went wrong
        .catch($scope.resetType);

    // initiate deferred execution
    defer.resolve();

    return defer.promise;
}

...

$scope.model = {
    item: getData()
};

Upvotes: 3

Views: 729

Answers (1)

ms87
ms87

Reputation: 17492

You can make your service such that it always returns a promise, if the data is available it will return the promise immediately otherwise after a REST call. For example your service might look like:

var dataThatMayOrMayNotBeAvailable=null;

var getDataThatMayOrMayNotBeAvailable=function(){
   var deferred = $q.defer();
   if(dataThatMayOrMayNotBeAvailable){
       deferred.resolve(dataThatMayOrMayNotBeAvailable);
    }else{
     $http({...}).success(function(data){
       dataThatMayOrMayNotBeAvailable=data;
       deferred.resolve(data);
      });
    }

return deferred.promise;

}

Usage:

getDataThatMayOrMayNotBeAvailable().then(function(data){
   console.log(data);
})

Upvotes: 3

Related Questions