简体   繁体   中英

Resolving multiple promises in 1 http request

My current implementation have multiple controllers accessing the same JSON dataset using a service. I want to be able to resolve multiple requests that comes in during an ongoing HTTP request , in a single query.

Service:

app.factory('dataService', function($http, $q) {
  var promises = [];
  var busy = false;
  return {
    getData: function() {
      if (busy) {
        var p = $q.defer();
        promises.push(p);
        return p.promise;
      }
      busy = true;
      var promise = $http.get('data.json').then(function(response) {
        while (promises.length > 0) {
          promises.pop().resolve(response.data);
        }
        busy = false;
        return response.data;
      }, function(error) {
        while (promises.length > 0) {
          promises.pop().reject(error);
        }
        busy = false;
      });
      return promise;
    }
  }
});

Controller:

app.controller('Ctrl1', function($scope, dataService) {
   $scope.logger = "";

   function getIt(a) {
     dataService.getData().then(function(d) {
      $scope.logger += 'Request '+a+': '+(new Date).getTime()+'\n';
   });
   }

   // These will be resolved in a 'batch'
   getIt(1);
   setTimeout(function(){getIt(2)},100);
   setTimeout(function(){getIt(3)},0);

   // This will be resolved in the 'next batch'
   setTimeout(function(){getIt(4)},2000);
   setTimeout(function(){getIt(5)},2100);

});

A plunker example.

Assuming the sequence of the request is not important, is there a better way to do this or are there any flaws that might lurk in this technique?

You don't actually need multiple promises here. You can just give out the same promise to each caller during the same turn:

app.factory('dataService', function($http, $q) {
  var busyPromise = null;
  return {
    getData: function() {
      if (!busyPromise) {
        busyPromise = $http.get('data.json').finally(function() {
          busyPromise = null;
        });
      }
      return busyPromise;
    }
  }
});

the code looks ok, but I think defer is deprecated, alse beware of the .then(success, fail) anti-pattern, for example, you are throwing error for all the waiting promises, it would be hidden for the actual one making the call. So I would change it to something like:

app.factory('dataService', function($http, $q) {
  var promises = [];
  var busy = false;
  return {
    getData: function() {
      if (busy) return new Promise((res, rej) => promises.push({res, rej}));
      busy = true;
      return $http.get('data.json').then(response => {
        promises.forEach( promise => promise.res(response.data));
        promises = [];
        busy = false;
      }).catch( e => {
        promises.forEach( promise => promise.rej(e));
        promises = [];
        busy = false;
        throw e;
      });
    }
  }
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM