Promises: Repeat operation until it succeeds?

前端 未结 6 843
天命终不由人
天命终不由人 2020-11-30 08:28

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

相关标签:
6条回答
  • 2020-11-30 09:06
    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);
    
        }
    })();
    
    0 讨论(0)
  • 2020-11-30 09:12

    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 :)

    0 讨论(0)
  • 2020-11-30 09:14

    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.

    0 讨论(0)
  • 2020-11-30 09:16

    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/

    0 讨论(0)
  • 2020-11-30 09:16

    My sugesstion would be to use the bluebird-retry library

    To install

    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);
        });
    

    The above program tries the promise flow 5 times with interval * backoff backoff interval between every retry.

    Also, should you require to pass any arguments, pass it as args which accepts array of arguments. Include it in options section where max_retries, interval and backoff is mentioned.

    Here is the official documentation https://www.npmjs.com/package/bluebird-retry

    0 讨论(0)
  • 2020-11-30 09:23

    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 */);
    
    0 讨论(0)
提交回复
热议问题