Execute várias tarefas de forma assíncrona e retorne o primeiro resultado bem-sucedido na função JavaScript

Eu tenho que escrever uma função javaScript que retorne alguns dados para o chamador.

Nessa função, tenho várias maneiras de recuperar dados, ou seja,

Pesquisa do cacheRecuperar do HTML5 LocalStorageRecuperar do REST Backend (bônus: coloque os dados atualizados novamente em cache)

Cada opção pode levar algum tempo para terminar e pode ter êxito ou falhar.

O que eu quero fazer é executar todas essas três opções de forma assíncrona / paralela e retornar o resultado quem retornar primeiro.

Entendo que a execução paralela não é possível no JavaScript, pois é de thread único, mas quero pelo menos executá-las de forma assíncrona e cancelar as outras tarefas se uma delas retornar resultado com êxito.

Eu tenho mais uma pergunta.

Retorne antecipadamente e continue executando a tarefa restante em uma função JavaScript.

Exemplo de pseudocódigo:

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;
}

Aconselhe como implementar esses requisitos em JavaScript.

Soluções descobertas até agora:Resultado mais rápidoSolução JavaScript ES6

Ref:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

Promise.race (iterável)

Retorna uma promessa que resolve quando a primeira promessa no iterável é resolvida.

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"
});
Solução Java / Groovy

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
}

Retorno Antecipado e Função Interativa

Promessas angulares combinadas com geradores ES6 ???
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();
    }
};
});

questionAnswers(3)

yourAnswerToTheQuestion