Promises: Repeat operation until it succeeds?

时光毁灭记忆、已成空白 提交于 2019-11-26 22:17:41

问题


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:


  1. Assign a boolean variable for "all process timeout".
  2. Call window's setTimeout to make that variable 'false' after that "all process timeout".
  3. Call promise operation with a timeout.
  4. If it succeeds no problem.
  5. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!