Подтвердить что ты не робот

Promises: повторять операцию до тех пор, пока она не завершится успешно?

Я хочу выполнить операцию несколько раз с увеличением таймаута между каждой операцией до тех пор, пока она не удастся или не истечет определенное количество времени. Как мне структурировать это с помощью promises в Q?

4b9b3361

Ответ 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;
}

Демо: http://jsfiddle.net/0dmzt53p/

Ответ 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))