【发布时间】:2014-12-28 22:36:30
【问题描述】:
我想重复执行一个操作,每次操作之间的超时时间会增加,直到它成功或经过一定的时间。如何在 Q 中使用 Promise 构建它?
【问题讨论】:
-
如果你想要一个带有原生承诺和最大重试次数的递归 sn-p,请检查:stackoverflow.com/a/44577075/3032209
标签: javascript promise q
我想重复执行一个操作,每次操作之间的超时时间会增加,直到它成功或经过一定的时间。如何在 Q 中使用 Promise 构建它?
【问题讨论】:
标签: javascript promise q
在我看来,这里的所有答案都非常复杂。 Kos 的想法是正确的,但您可以通过编写更惯用的 promise 代码来缩短代码:
function retry(operation, delay) {
return operation().catch(function(reason) {
return Q.delay(delay).then(retry.bind(null, operation, delay * 2));
});
}
还有 cmets:
function retry(operation, delay) {
return operation(). // run the operation
catch(function(reason) { // if it fails
return Q.delay(delay). // delay
// retry with more time
then(retry.bind(null, operation, delay * 2));
});
}
如果你想在一定时间后超时(比如说 10 秒,你可以简单地这样做:
var promise = retry(operation, 1000).timeout(10000);
该功能已内置于 Q 中,无需重新发明 :)
【讨论】:
Q.delay(N) 替换为new Promise(r => setTimeout(r, N)),从此过上幸福的生活:)
以下是我如何处理此问题的示例,其中包含一些辅助函数。请注意,“maxTimeout”是更复杂的部分,因为您必须在两个状态之间进行竞赛。
// Helper delay function to wait a specific amount of time.
function delay(time){
return new Promise(function(resolve){
setTimeout(resolve, time);
});
}
// A function to just keep retrying forever.
function runFunctionWithRetries(func, initialTimeout, increment){
return func().catch(function(err){
return delay(initialTimeout).then(function(){
return runFunctionWithRetries(
func, initialTimeout + increment, increment);
});
});
}
// Helper to retry a function, with incrementing and a max timeout.
function runFunctionWithRetriesAndMaxTimeout(
func, initialTimeout, increment, maxTimeout){
var overallTimeout = delay(maxTimeout).then(function(){
// Reset the function so that it will succeed and no
// longer keep retrying.
func = function(){ return Promise.resolve() };
throw new Error('Function hit the maximum timeout');
});
// Keep trying to execute 'func' forever.
var operation = runFunctionWithRetries(function(){
return func();
}, initialTimeout, increment);
// Wait for either the retries to succeed, or the timeout to be hit.
return Promise.race([operation, overallTimeout]);
}
然后要使用这些帮助程序,您需要执行以下操作:
// Your function that creates a promise for your task.
function doSomething(){
return new Promise(...);
}
runFunctionWithRetriesAndMaxTimeout(function(){
return doSomething();
}, 1000 /* start at 1s per try */, 500 /* inc by .5s */, 30000 /* max 30s */);
【讨论】:
我认为你不能在 Promise 级别上做到这一点 - Promise 不是一个操作,而只是一个将在未来到达的值,所以你不能定义一个类型为 Promise -> Promise 的函数,它将实现它。
您需要向下一层定义一个类型为 Operation -> Promise 的函数,其中 Operation 本身的类型为 () -> Promise。我假设该操作不采用任何参数 - 您可以预先部分应用它们。
这是一种递归方法,每次运行都会使超时时间加倍:
function RepeatUntilSuccess(operation, timeout) {
var deferred = Q.defer();
operation().then(function success(value) {
deferred.resolve(value);
}, function error(reason) {
Q.delay(timeout
.then(function() {
return RepeatUntilSuccess(operation, timeout*2);
}).done(function(value) {
deferred.resolve(value);
});
});
return deferred.promise;
}
【讨论】:
类似这样的:
var goOn= true;
setTimeout(function () {
goOn= false;
}, 30000); // 30 seconds -- all process timeout
var timeout = 1000; // 1 second
(function () {
var helperFunction = function () {
callAsyncFunc().then(function () {
// success...
}, function () {
// fail
if (goOn) {
timeout += 1000; // increase timeout 1 second
helperFunction();
}
}).timeout(timeout);
}
})();
【讨论】:
.timeout 的作用感到困惑(它实际上做了你使用goOn 的事情,因为它只是内置在库中)。
我用 Promises/A+ 做了以下事情(用 Q 应该没问题)
function delayAsync(timeMs)
{
return new Promise(function(resolve){
setTimeout(resolve, timeMs);
});
}
//use an IIFE so we can have a private scope
//to capture some state
(function(){
var a;
var interval = 1000;
a = function(){
return doSomethingAsync()
.then(function(success){
if(success)
{
return true;
}
return delayAsync(interval)
.then(function(){
interval *= 2;
})
.then(a());
});
};
a();
})();
我相信您可以弄清楚如何在最大超时后保释。
【讨论】:
a吗?
我的建议是使用 bluebird-retry 库
安装
npm i bluebird-retry
var Promise = require('bluebird');
var retry = require('bluebird-retry');
var count = 0;
function myfunc() {
console.log('myfunc called ' + (++count) + ' times '+new Date().toISOString());
return Promise.reject(new Error(''));
}
retry(myfunc,{ max_tries: 5, interval: 5000, backoff: 2 })
.then(function(result) {
console.log(result);
});
上述程序以interval * backoff每次重试之间的退避间隔尝试承诺流5次。
另外,如果您需要传递任何参数,请将其作为args 传递,它接受参数数组。将其包含在提及max_retries、interval 和backoff 的选项部分中。
【讨论】: