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.,
- Lookup from cache
- Retrieve from HTML5 LocalStorage
- Retrieve from REST Backend (bonus: put the fresh data back into cache)
Each option may take its own time to finish and it may succeed or fail.
What I want to do is, to execute all those three options asynchronously/parallely and return the result whoever return first.
I understand that parallel execution is not possible in JavaScript since it is single threaded, but I want to at least execute them asynchronously and cancel the other tasks if one of them return successfully result.
I have one more question.
Early return and continue executing the remaining task in a JavaScript function.
Example pseudo code:
function getOrder(id) {
var order;
// early return if the order is found in cache.
if (order = cache.get(id)) return order;
// continue to get the order from the backend REST API.
order = cache.put(backend.get(id));
return order;
}
Please advice how to implement those requirements in JavaScript.
Solutions discovered so far:
Fastest Result
JavaScript ES6 solution
Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Promise.race(iterable)
Returns a promise that resolves when the first promise in the iterable resolves.
var p1 = new Promise(function(resolve, reject) { setTimeout(resolve, 500, "one"); });
var p2 = new Promise(function(resolve, reject) { setTimeout(resolve, 100, "two"); });
Promise.race([p1, p2]).then(function(value) {
// value == "two"
});
Java/Groovy solution
Ref: http://gpars.org/1.1.0/guide/guide/single.html
import groovyx.gpars.dataflow.Promise
import groovyx.gpars.dataflow.Select
import groovyx.gpars.group.DefaultPGroup
import java.util.concurrent.atomic.AtomicBoolean
/**
* Demonstrates the use of dataflow tasks and selects to pick the fastest result of concurrently run calculations.
* It shows a waz to cancel the slower tasks once a result is known
*/
final group = new DefaultPGroup()
final done = new AtomicBoolean()
group.with {
Promise p1 = task {
sleep(1000)
if (done.get()) return
10 * 10 + 1
}
Promise p2 = task {
sleep(1000)
if (done.get()) return
5 * 20 + 2
}
Promise p3 = task {
sleep(1000)
if (done.get()) return
1 * 100 + 3
}
final alt = new Select(group, p1, p2, p3, Select.createTimeout(500))
def result = alt.select()
done.set(true)
println "Result: " + result
}
Early Return and Interactive Function
Angular Promises combined with ES6 generators???
angular.module('org.common') .service('SpaceService', function ($q, $timeout, Restangular, $angularCacheFactory) { var _spacesCache = $angularCacheFactory('spacesCache', { maxAge: 120000, // items expire after two min deleteOnExpire: 'aggressive', onExpire: function (key, value) { Restangular.one('organizations', key).getList('spaces').then(function (data) { _spacesCache.put(key, data); }); } }); /** * @class SpaceService */ return { getAllSpaces: function (orgId) { var deferred = $q.defer(); var spaces; if (spaces = _spacesCache.get(orgId)) { deferred.resolve(spaces); } else { Restangular.one('organizations', orgId).getList('spaces').then(function (data) { _spacesCache.put(orgId, data); deferred.resolve(data); } , function errorCallback(err) { deferred.reject(err); }); } return deferred.promise; }, getAllSpaces1: function (orgId) { var deferred = $q.defer(); var spaces; var timerID = $timeout( Restangular.one('organizations', orgId).getList('spaces').then(function (data) { _spacesCache.put(orgId, data); deferred.resolve(data); }), function errorCallback(err) { deferred.reject(err); }, 0); deferred.notify('Trying the cache now...'); //progress notification if (spaces = _spacesCache.get(orgId)) { $timeout.cancel(timerID); deferred.resolve(spaces); } return deferred.promise; }, getAllSpaces2: function (orgId) { // set up a dummy canceler var canceler = $q.defer(); var deferred = $q.defer(); var spaces; $timeout( Restangular.one('organizations', orgId).withHttpConfig({timeout: canceler.promise}).getList('spaces').then(function (data) { _spacesCache.put(orgId, data); deferred.resolve(data); }), function errorCallback(err) { deferred.reject(err); }, 0); if (spaces = _spacesCache.get(orgId)) { canceler.resolve(); deferred.resolve(spaces); } return deferred.promise; }, addSpace: function (orgId, space) { _spacesCache.remove(orgId); // do something with the data return ''; }, editSpace: function (space) { _spacesCache.remove(space.organization.id); // do something with the data return ''; }, deleteSpace: function (space) { console.table(space); _spacesCache.remove(space.organization.id); return space.remove(); } }; });