2015-02-11 102 views
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; 

     } 
    }; 
}); 

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

回答

5

您可以通过使用$http的超时属性的做到这一点,同时使用requestresponseError回调保存并分别执行各自$http请求。

步骤:

  1. 懒洋洋地注入request回调过程中的$http服务,这将是获得$http服务,因为在工厂的功能注入它会导致循环依赖的唯一途径。

  2. 确定是否已在request回调中传递的配置已处理。如果尚未处理,请将该配置添加到请求堆栈中并按优先级进行排序。在配置对象的超时属性中添加已解析的承诺,以取消当前的$http请求。最后返回配置对象。

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

DEMO

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回调的解决方案是这样的:

DEMO

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); 
} 
+0

这是我正在寻找的解决方案类型。我会尽力实施它。谢谢! – krystiangw 2015-02-11 20:31:11

+0

ryeballar我喜欢你的答案,但你放在请求堆栈中的诺言一旦复制似乎不会执行先前承诺的代码。真正的更优雅的解决方案是在您将请求放入堆栈的时候推迟执行,但我找不到一个好的方法来执行此操作。 – 2015-11-04 10:13:44

+0

你能提供一个等效的plnkr或者或者jsfiddle这种情况吗? – ryeballar 2015-11-05 02:45:42

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; 

好像它过得好$间隔丢失。以及你的延期实例全局设置为var之前。

+0

是的,你是对的工作。但正如我所说的,问题是我不能在这里回复诺言。它只接受'配置'对象。 – krystiangw 2015-02-11 10:26:35

+0

在$ http.get方法中稍微挖了一点,没有看到在此设置优先级的机会。也许在调用$ http.get之前创建一个自己的服务来处理这个和优先级? 'service.httpGet(url,{priority:1});'? – graphefruit 2015-02-11 11:20:23

0

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

这不会对以下使用情况 Angular Http Priority