我想重复执行一个操作,每次操作之间的超时时间增加,直到成功或经过一定的时间。我如何用Q中的承诺来构建它?
答案 0 :(得分:24)
我认为这里的所有答案都非常复杂。 Kos有正确的想法,但您可以通过编写更多惯用的承诺代码来缩短代码:
function retry(operation, delay) {
return operation().catch(function(reason) {
return Q.delay(delay).then(retry.bind(null, operation, delay * 2));
});
}
并发表评论:
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中,无需重新发明它:)
答案 1 :(得分:3)
以下是我如何通过一些帮助函数来解决这个问题的一个例子。注意,' 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 */);
答案 2 :(得分:2)
我认为你不能在承诺级别上做 - 承诺不是一个操作,而是一个将来会到来的价值,所以你不能定义一个键入的函数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;
}
答案 3 :(得分:0)
我使用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();
})();
我确信你可以在最大超时后找出如何保释。
答案 4 :(得分:0)
这样的事情:
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);
}
})();
答案 5 :(得分:0)
我的建议是使用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
的选项部分。