问题
I want to perform an operation repeatedly, with an increasing timeout between each operation, until it succeeds or a certain amount of time elapses. How do I structure this with promises in Q?
回答1:
All the answers here are really complicated in my opinion. Kos has the right idea but you can shorten the code by writing more idiomatic promise code:
function retry(operation, delay) {
return operation().catch(function(reason) {
return Q.delay(delay).then(retry.bind(null, operation, delay * 2));
});
}
And with comments:
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));
});
}
If you want to time it out after a certain time (let's say 10 seconds , you can simply do:
var promise = retry(operation, 1000).timeout(10000);
That functionality is built right into Q, no need to reinvent it :)
回答2:
Here's an example of how I'd approach this, with some helper functions. Note, the 'maxTimeout' is the more complicated part because you have to race two states.
// 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]);
}
Then to use these helpers, you'd do something like this:
// 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 */);
回答3:
I think you can't do it on promise level - a promise isn't an operation, but is just a value that's going to arrive in the future, so you can't define a function typed Promise -> Promise
that will achieve it.
You'd need to go one level down and define a function typed Operation -> Promise
where Operation is itself typed () -> Promise
. I assume the operation doesn't take any parameters - you can partially-apply them beforehand.
Here's a recursive approach that doubles the timeout on every run:
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;
}
Demo: http://jsfiddle.net/0dmzt53p/
回答4:
- Assign a boolean variable for "all process timeout".
- Call window's setTimeout to make that variable 'false' after that "all process timeout".
- Call promise operation with a timeout.
- If it succeeds no problem.
- If it fails: In promise's error handler, call promise function again with an increased timeout if the boolean variable is true.
Something like this:
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:
I did the following with Promises/A+ (which should be fine with 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();
})();
I'm sure you could figure out how to bail after a maximum timeout.
来源:https://stackoverflow.com/questions/26694467/promises-repeat-operation-until-it-succeeds