I have to write a javaScript function that return some data to the caller.
In that function I have multiple ways to retrieve data i.e.,
Personally, I would try the three asynchronous retrievals sequentially, starting with the least expensive and ending with the most expensive. However, responding to the first of three parallel retrievals is an interesting problem.
You should be able to exploit the characteristic of $q.all(promises)
, by which :
But you want to invert the logic such that :
This should be achievable with an invert()
utility which converts success to failure and vice versa.
function invert(promise) {
return promise.then(function(x) {
return $q.defer().reject(x).promise;
}, function(x) {
return $q.defer().resolve(x).promise;
});
}
And a first()
utility, to give the desired behaviour :
function first(arr) {
return invert($q.all(arr.map(invert)));
}
Notes:
arr
is an array of promisesarray.map()
is assumed (otherwise you can explicitly loop to achieve the same effect)invert()
in first()
restores the correct sense of the promise it returnsThen getOrder()
will be something like this :
function getOrder(id) {
return first([
cache.get(id),
localStorage.get(id).then(cache.put),
backend.get(id).then(cache.put).then(localStorage.put)
]);
}
Thus, getOrder(id)
should return a Promise of an order (not the order directly).
The problem in your example getOrder
lies in that if the 3 lookup functions are going to be asynchronous, you won't get the order back from them right away and as they are not blocking, the getOrder
would return null
; You would be better off defining a callback function which takes action on the first returned order
data and simply ignores the rest of them.
var doSomethingWithTheOrder = function CallBackOnce (yourResult) {
if (!CallBackOnce.returned) {
CallBackOnce.returned = true;
// Handle the returned data
console.log('handle', yourResult);
} else {
// Ignore the rest
console.log('you are too late');
}
}
Make your data lookup functions accept a callback
function cacheLookUp(id, callback) {
// Make a real lookup here
setTimeout(function () {
callback('order data from cache');
}, 3000);
}
function localeStorageLookUp(id, callback) {
// Make a real lookup here
setTimeout(function () {
callback('order data from locale storage');
}, 1500);
}
function restLookUp(id, callback) {
// Make a real lookup here
setTimeout(function () {
callback('order data from rest');
}, 5000);
}
And pass the callback function to each of them
function getOrder(id) {
cacheLookUp(id, doSomethingWithTheOrder);
localeStorageLookUp(id, doSomethingWithTheOrder);
restLookUp(id, doSomethingWithTheOrder);
}
Create a broadcast event in your api calls, then create $scope.$on to listen to those broadcast, when $on get's activated do the function that refreshes those objects.
So in your service have a function that makes an ajax calls to your rest api. You would have 3 ajax calls. And 3 listeners. Each of them would look something like this.
This is just sudo code, but this format is how you do it something like
$http({
method: "GET",
url: url_of_api,
}).success(function(data, *args, *kwargs){
$rooteScope.$braodcast('success', data)
})
In your controller have a listener something like this
$scope.$on('success', function(event, args){
// Check the state of the other objects, have they been refreshed - you probably want to set flags to check
if (No Flags are Set):
$scope.data = args // which would be the returned data adn $scope.data would be what you're trying to refresh.
}