我正在处理具有大量惰性数据加载的应用程序。我想根据“优先级”参数对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; } }; });
但是我不能在这里退还诺言。有任何想法吗?
您可以通过使用$http的timeout属性来做到这一点,并使用request和responseError回调$http分别保存和执行每个请求。
$http
request
responseError
脚步:
延迟地$http在request回调过程中注入服务,这将是获得$http服务的唯一方法,因为在工厂函数中注入服务会导致循环依赖。
确定在request回调中传递的配置是否已处理。如果尚未处理,则将配置添加到请求堆栈中,并按优先级对其进行排序。在配置对象的timeout属性中添加一个已解决的Promise,以取消当前$http请求。最后返回配置对象。
一旦$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); }
更新06/16/2019
如评论中所述,优先请求返回的承诺不会返回预期的承诺解决方案或拒绝。我已经通过以下方式更新了拦截器以适应这种情况:
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); });