3

我正在开发一个具有大量延迟数据加载的应用程序。我想根据“优先级”参数对 http 请求进行优先级排序。

这是使用它的概念。

$http.get(url, {params: query, priority: 1})

我正在考虑使用 $http 拦截器。像这样的东西:

 angular.module('myModule')
.factory('httpPriorityInterceptor', function ($interval, $q) {
    var requestStack = [];

    return {
        request: function (config) {

            config.priority = config.priority || 3;

            requestStack.push(config);
            requestStack.sort(sortByPriority);

            if (isFirstToGo(item)) return requestStack.pop();

            deferred = $q.defer();

            var intervalPromise = $interval(function(){

                if (isFirstToGo(item)) {
                    deferred.resolve(requestStack.pop());
                    $interval.cancel(intervalPromise);
                };

            }, 100);

            return deferred.promise;

        }   
    };
});

但我不能在这里回报承诺。有任何想法吗?

4

3 回答 3

4

您可以通过使用$http的 timeout 属性来做到这一点,并使用requestresponseError回调分别保存和执行每个$http请求。

脚步:

  1. 在回调过程中延迟注入$http服务request,这将是获取$http服务的唯一方法,因为在工厂函数中注入它会导致循环依赖。

  2. 判断request回调中传入的配置是否已经处理。如果尚未处理,则将配置添加到请求堆栈中并按优先级排序。在配置对象的 timeout 属性中添加已解析的 promise,以取消当前$http请求。最后返回配置对象。

  3. 一旦$http请求被取消,在responseError回调中捕获它。如果请求堆栈中有项目,则弹出第一项(配置)并使用延迟加载$http服务调用它。最后使用回调提供的拒绝参数返回一个被拒绝的承诺。

演示

angular.module('demo', [])

  .config(function($httpProvider) {
    $httpProvider.interceptors.push('httpPriorityInterceptor');
  })

  .factory('httpPriorityInterceptor', function($q, $injector) {


    var requestStack = [], // request stack
        $http = null; // http service to be lazy loaded

    return {
      request: request, // request callback
      responseError: responseError // responseError callback
    };

    // comparison function to sort request stack priority
    function sort(config1, config2) {
      return config1.priority < config2.priority;
    }

    function request(config) {

      // Lazy load $http service
      if(!$http) {
        $http = $injector.get('$http');
      }

      // check if configuration has not been requested
      if(!config.hasBeenRequested) {

        // set indicator that configuration has been requested
        config.hasBeenRequested = true;

        // set default priority if not present
        config.priority = config.priority || 3;

        // add a copy of the configuration
        // to prevent it from copying the timeout property
        requestStack.push(angular.copy(config));

        // sort each configuration by priority
        requestStack = requestStack.sort(sort);

        // cancel request by adding a resolved promise
        config.timeout = $q.when();
      }

      // return config
      return config;
    }


    function responseError(rejection) {

      // check if there are requests to be processed
      if(requestStack.length > 0) {

        // pop the top most priority
        var config = requestStack.pop();
        console.log(config);

        // process the configuration
        $http(config);
      }

      // return rejected request
      return $q.reject(rejection);
    }

  })

  .run(function($http) {

    // create http request
    var createRequest = function(priority) {
      $http.get('/priority/' + priority, {priority: priority});
    };

    createRequest(3);
    createRequest(1);
    createRequest(4);
    createRequest(2);

  });

要确保以正确的顺序调用每个请求,您可以检查控制台选项卡中的日志或网络选项卡中的请求。

更新:

如果您希望按顺序调用请求(当第一个请求必须在下一个请求调用之前完成时),那么您可以在responseError回调中调整我的解决方案,如下所示:

演示

function responseError(rejection) {

  // check if there are requests to be processed
  if(requestStack.length > 0) {

    requestStack.reduceRight(function(promise, config) {
      return promise.finally(function() {
        return $http(config);
      });
    }, $q.when());

    requestStack.length = 0;

  }

  // return rejected request
  return $q.reject(rejection);
}

2019 年 6 月 16 日更新

正如评论中提到的,优先请求返回的承诺不会返回预期的承诺解决或拒绝。我通过以下方式更新了拦截器以适应这种情况:

  1. 保存相对于每个 http 配置的延迟承诺。
  2. 在拦截器中返回延迟的承诺,以responseError保持请求的解决或拒绝。
  3. 最后在优先请求的迭代中使用延迟承诺。

演示

angular.module('demo', [])

  .config(function($httpProvider) {
    $httpProvider.interceptors.push('httpPriorityInterceptor');
  })

  .factory('httpPriorityInterceptor', function($q, $injector) {


    var requestStack = [], // request stack
        $http = null; // http service to be lazy loaded

    return {
      request: request, // request callback
      responseError: responseError // responseError callback
    };

    // comparison function to sort request stack priority
    function sort(config1, config2) {
      return config1.priority < config2.priority;
    }

    function request(config) {

      // Lazy load $http service
      if(!$http) {
        $http = $injector.get('$http');
      }

      // check if configuration has not been requested
      if(!config.hasBeenRequested) {

        // set indicator that configuration has been requested
        config.hasBeenRequested = true;

        // set default priority if not present
        config.priority = config.priority || 3;

        // add a defered promise relative to the config requested
        config.$$defer = $q.defer();

        // add a copy of the configuration
        // to prevent it from copying the timeout property
        requestStack.push(angular.copy(config));

        // sort each configuration by priority
        requestStack = requestStack.sort(sort);

        // cancel request by adding a resolved promise
        config.timeout = $q.when();
      }

      // return config
      return config;
    }


    function responseError(rejection) {

      // check if there are requests to be processed
      if(requestStack.length > 0) {

        requestStack.reduceRight(function(promise, config) {
          var defer = config.$$defer;
          delete config.$$defer;
          return promise.finally(function() {
            return $http(config)
              .then(function(response) {
                defer.resolve(response);
              })
              .catch(function(error) {
                defer.reject(error);
              });

          });
        }, $q.when());

        requestStack.length = 0;

      }

      return rejection.config.$$defer.promise;
    }

  })

  .run(function($http) {

    // create http request
    var createRequest = function(priority) {
      return $http.get(priority + '.json', {priority: priority});
    };

    createRequest(3);
    createRequest(1).then(function(data) { console.log(data); })
    createRequest(4);
    createRequest(2);

  });
于 2015-02-11T12:30:44.447 回答
1

尝试包装你的超时

var deferred = $q.defer();
       (function (_deferred){
        var intervalPromise = $interval(function(){

            if (isFirstToGo(item)) {
                _defferred.resolve(requestStack.pop());
                $interval.cancel(intervalPromise);
            };

        }, 100);
        })(deferred);
return deferred.promise;

似乎它在 $interval 上迷路了。以及你的 deferred 是在 globalyvar之前设置的。

于 2015-02-11T09:38:16.917 回答
0

这不是正确的解决方案。您可以通过编写自己的服务来实现此目的,以便在调用 http get 之前优先考虑您的 api 调用队列。

这不适用于以下用例 Angular Http Priority

于 2017-03-23T09:50:15.100 回答