Execute multiple tasks asynchronously and return first successful result in JavaScript function

后端 未结 3 1484
别那么骄傲
别那么骄傲 2021-01-05 16:27

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.,

  1. Lookup from ca
相关标签:
3条回答
  • 2021-01-05 16:51

    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 :

    • as soon as any of the promises fails then the returned promise is rejected
    • if all promises are successful then the returned promise is resolved.

    But you want to invert the logic such that :

    • as soon as any of the promises is successful then the returned promise is resolved
    • if all promises fail then the returned promise is rejected.

    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:

    • the input arr is an array of promises
    • a native implementation of array.map() is assumed (otherwise you can explicitly loop to achieve the same effect)
    • the outer invert() in first() restores the correct sense of the promise it returns
    • I'm not particularly experienced in angular, so I may have made syntactic errors - however I think the logic is correct.

    Then 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).

    0 讨论(0)
  • 2021-01-05 17:05

    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);
    }
    
    0 讨论(0)
  • 2021-01-05 17:17

    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.
    }
    
    0 讨论(0)
提交回复
热议问题