如何在 angular $http 服务中确定请求的优先级?
How to prioritize requests in angular $http service?
我正在开发一个具有大量延迟数据加载的应用程序。我想根据 'priority' 参数对 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;
}
};
});
但我不能return在这里承诺。有任何想法吗?
尝试包装你的超时
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 上迷路了。以及你的延迟在全局实例化之前设置了一个var
。
您可以通过使用 $http
的超时 属性 来完成此操作,并使用 request
和 responseError
回调来保存和执行每个 $http
分别请求。
步骤:
在 request
回调过程中延迟注入 $http
服务,这将是获得 $http
服务的唯一方法,因为它是在工厂的函数导致循环依赖。
判断request
回调中传入的配置是否已经处理。如果还没有处理则在请求栈中添加配置,并按优先级排序。在配置对象的timeout属性中添加resolved promise,取消当前$http
请求。最后 return 配置对象。
一旦 $http
请求被取消,在 responseError
回调中捕获它。如果请求堆栈中有项目,弹出第一个项目(配置)并使用延迟加载 $http
服务调用它。最后 return 使用回调提供的拒绝参数拒绝承诺。
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 日更新
如评论中所述,优先请求 return 承诺不会 return 预期的承诺解决或拒绝。我已经通过以下方式更新了拦截器以适应这种情况:
- 保存与每个 http 配置相关的延迟承诺。
- Return
responseError
拦截器中的延迟承诺,以保持对请求的解决或拒绝。
- 最终在优先请求的迭代中使用延迟承诺。
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);
});
这不是正确的解决方案。您可以通过编写自己的服务来实现此目的,以便在调用 http get 之前确定 api 调用队列的优先级。
这不适用于以下用例
我正在开发一个具有大量延迟数据加载的应用程序。我想根据 'priority' 参数对 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;
}
};
});
但我不能return在这里承诺。有任何想法吗?
尝试包装你的超时
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 上迷路了。以及你的延迟在全局实例化之前设置了一个var
。
您可以通过使用 $http
的超时 属性 来完成此操作,并使用 request
和 responseError
回调来保存和执行每个 $http
分别请求。
步骤:
在
request
回调过程中延迟注入$http
服务,这将是获得$http
服务的唯一方法,因为它是在工厂的函数导致循环依赖。判断
request
回调中传入的配置是否已经处理。如果还没有处理则在请求栈中添加配置,并按优先级排序。在配置对象的timeout属性中添加resolved promise,取消当前$http
请求。最后 return 配置对象。一旦
$http
请求被取消,在responseError
回调中捕获它。如果请求堆栈中有项目,弹出第一个项目(配置)并使用延迟加载$http
服务调用它。最后 return 使用回调提供的拒绝参数拒绝承诺。
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 日更新
如评论中所述,优先请求 return 承诺不会 return 预期的承诺解决或拒绝。我已经通过以下方式更新了拦截器以适应这种情况:
- 保存与每个 http 配置相关的延迟承诺。
- Return
responseError
拦截器中的延迟承诺,以保持对请求的解决或拒绝。 - 最终在优先请求的迭代中使用延迟承诺。
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);
});
这不是正确的解决方案。您可以通过编写自己的服务来实现此目的,以便在调用 http get 之前确定 api 调用队列的优先级。
这不适用于以下用例