I need to write a javaScript function that returns some data to the caller.
In this function, I have several ways to retrieve data, i.e.
- Cache Search
- Get from HTML5 LocalStorage
- Get from REST Backend (bonus: return new data to the cache)
Each option may take its time to complete, and it may succeed or fail.
I want to do to execute all these three parameters asynchronously / in parallel and return the result to those who return first.
I understand that parallel execution is not possible in JavaScript, because it is single-threaded, but I want to at least execute them asynchronously and cancel other tasks if one of them returns the result successfully.
I've got one more question.
Return early and continue with the remaining task in the JavaScript function.
Pseudo-code example:
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; }
Consult how to implement these requirements in JavaScript.
Solutions discovered so far:
Promise.race (iterable)
Returns the promise that is resolved when the first promise is resolved in the fighter.
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) {
Java / Groovy Solution
Link: 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 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 Feedback and Interactive Feature
Angular Promises in combination 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(); } }; });