Выполнить несколько задач асинхронно и вернуть первый успешный результат в функции JavaScript

Я должен написать функцию javaScript, которая возвращает некоторые данные вызывающей стороне.

В этой функции у меня есть несколько способов получения данных, т.е.

Поиск из кэшаПолучить из HTML5 LocalStorageПолучить из REST Backend (бонус: положить свежие данные обратно в кеш)

Каждый вариант может занять свое время, чтобы завершиться, и он может быть успешным или неудачным.

Что я хочу сделать, так это выполнить все эти три параметра асинхронно / параллельно и вернуть результат тому, кто вернется первым.

Я понимаю, что параллельное выполнение невозможно в JavaScript, поскольку оно является однопоточным, но я хочу, по крайней мере, выполнить их асинхронно и отменить другие задачи, если одна из них вернет результат успешно.

У меня есть еще один вопрос.

Ранний возврат и продолжение выполнения оставшейся задачи в функции JavaScript.

Пример псевдокода:

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

Пожалуйста, посоветуйте, как реализовать эти требования в JavaScript.

Решения, обнаруженные до сих пор:Самый быстрый результатРешение JavaScript ES6

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

Promise.race (итерация)

Возвращает обещание, которое разрешается, когда первое обещание в итерируемом разрешается.

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 решение

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
}

Раннее возвращение и интерактивная функция

Угловые обещания в сочетании с генераторами 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();
    }
};
});

Ответы на вопрос(3)

Ваш ответ на вопрос