Skip to content

Instantly share code, notes, and snippets.

@thom4parisot
Forked from fcamblor/DataLoader.js
Last active April 19, 2016 11:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thom4parisot/41141f21f2c0895c722dfe1fec15afd9 to your computer and use it in GitHub Desktop.
Save thom4parisot/41141f21f2c0895c722dfe1fec15afd9 to your computer and use it in GitHub Desktop.
Resolving promise multiple times

Context

Voxxrin is an OSS mobile-oriented application dedicated to conferences, allowing to browse presentations. Presentations may come from 2 sources :

  • Either from a backend
  • Or from localforage (if a previous call to the backend was made "recently", "recently" here corresponds to ~1day)

I'd like to implement a single method utility allowing to address both data resolutions at the same time, afterall, we should consider this as a single promise which may be resolved multiple times :

  • Only once if localforage is empty (the first time we run the application)
  • Only once if localforage is filled and we already called the backend "recently"
  • Twice if localforage is filled and the backend was called some time ago, in that particular case, I'd like :
    • To quickly return data coming from cache
    • Run in the background a call to the server, and resolve the promise again once data is fetched (and in the case where backend is not reachable -for instance because user has connectivity issues-, user will still have presentations coming from cache)

I guess this is a very common pattern of "serve my data from the cache first, then try to refresh it from an http call"

Currently, I achieved to implement this by returning an "array of standard promises", because I didn't found any library on the www, allowing to achieve this easily. What I dislike in that approach is the caller (MyAngularCtrl) needs to wrap the method call (SharedData.loadPresentationsForEventPromises()) with an iteration, which :

  • doesn't seem very natural
  • imply to name the method weirdly : calling it loadPresentationsForEventPromises() instead of loadPresentationsForEvent()
'use strict';
angular.module('voxxrin')
.controller('PresentationsCtrl', function ($scope, $stateParams, SharedData) {
Promise.all(SharedData.loadPresentationsForEventPromises($stateParams.eventId)).then(function(presentations){
$scope.presentations = presentations;
});
});
angular.module('voxxrin')
.service('SharedData', function ($localForage, $q, Presentations) {
var SharedData = {
_perEventCachedData: {},
loadPresentationsForEventPromises: function(eventId) {
var self = this;
var promises = [];
// If we didn't fetched presentations "recently", fetching it
if(this.outdatedCachedDataForEvent(eventId)) {
var updatedDataDefer = $q.defer();
Presentations.fromEvent(eventId).$promise.then(function(presentations) {
var storedData = {
presentations: presentations,
outDatedOn: moment().add(1, 'days').valueOf()
};
// Updating localforage data
$localForage.setItem('data-for-event-' + eventId, storedData).then(function(){
self._perEventCachedData[eventId] = storedData;
updatedDataDefer.resolve(presentations);
});
}).catch(function(){
console.error("Fetching presentations from server failed");
updatedDataDefer.reject();
});
promises.push(updatedDataDefer.promise);
}
var cacheDefer = $q.defer();
if(self._perEventCachedData[eventId]) {
cacheDefer.resolve(self._perEventCachedData[eventId].presentations);
} else {
$localForage.getItem('data-for-event-'+eventId).then(function(storedEventData){
if(storedEventData) {
self._perEventCachedData[eventId] = storedEventData;
cacheDefer.resolve(storedEventData.presentations);
} else {
cacheDefer.reject();
}
}).catch(function(){
cacheDefer.reject();
});
}
promises.push(cacheDefer.promise);
return promises;
},
outdatedCachedDataForEvent: function(eventId){
if(!this._perEventCachedData[eventId]) {
return true;
}
if(moment(this._perEventCachedData[eventId].outDatedOn).isBefore(moment())) {
return true;
}
return false;
}
};
return SharedData;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment