Я хочу выполнить операцию несколько раз с увеличением таймаута между каждой операцией до тех пор, пока она не удастся или не истечет определенное количество времени. Как мне структурировать это с помощью promises в Q?
Promises: повторять операцию до тех пор, пока она не завершится успешно?
Ответ 1
Все ответы здесь действительно сложны, на мой взгляд. Кос имеет правильную идею, но вы можете сократить код, написав более идиоматический код обещания:
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, нет необходимости ее изобретать:)
Ответ 2
Я думаю, что вы не можете сделать это на уровне обещаний - обещание - это не операция, а просто ценность, которая придет в будущем, поэтому вы не можете определить функцию, набранную Promise -> Promise
, которая будет достичь этого.
Вам нужно перейти на один уровень вниз и определить функцию, набранную Operation -> Promise
, где сама операция набрана () -> 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
Вот пример того, как я подхожу к этому, с некоторыми вспомогательными функциями. Обратите внимание, что "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 */);
Ответ 4
Я сделал следующее с 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();
})();
Я уверен, что вы могли бы понять, как залог после максимального таймаута.
Ответ 5
- Назначьте логическую переменную для "всего тайм-аута процесса".
- Вызовите окно setTimeout, чтобы сделать эту переменную "false" после этого "весь тайм-аут процесса".
- Операция вызова с таймаутом.
- Если это не удастся.
- Если это не удается: в обработчике ошибок обещания снова вызовите функцию обещания с увеличенным таймаутом, если логическая переменная истинна.
Что-то вроде этого:
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);
}
})();
Ответ 6
Вы также можете использовать этот open source компонент повторения-повтора, который повторяет функцию, которая возвращает обещание.
Пример:
promiseRetry((retry, number) => promiseFunction().catch(retry),{retries:3})
.then((result) => console.log(result)).catch(err => console.log(err))