承诺:重复操作直到成功?

时间:2014-11-01 23:44:18

标签: javascript promise q

我想重复执行一个操作,每次操作之间的超时时间增加,直到成功或经过一定的时间。我如何用Q中的承诺来构建它?

6 个答案:

答案 0 :(得分:24)

我认为这里的所有答案都非常复杂。 Kos有正确的想法,但您可以通过编写更多惯用的承诺代码来缩短代码:

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中,无需重新发明它:)

答案 1 :(得分: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 */);

答案 2 :(得分:2)

我认为你不能在承诺级别上做 - 承诺不是一个操作,而是一个将来会到来的价值,所以你不能定义一个键入的函数Promise -> Promise这将实现它。

您需要向下一级并定义一个类型为Operation -> Promise的函数,其中Operation本身已键入() -> 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 :(得分:0)

我使用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();
})();

我确信你可以在最大超时后找出如何保释。

答案 4 :(得分:0)

  1. 为"所有进程超时"。
  2. 分配一个布尔变量
  3. 调用窗口的setTimeout使该变量变为“假”'之后"所有过程超时"。
  4. 超时调用承诺操作。
  5. 如果成功没问题。
  6. 如果失败:在promise的错误处理程序中,如果布尔变量为真,则再次调用promise函数并增加超时。
  7. 这样的事情:

    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 :(得分:0)

我的建议是使用bluebird-retry

要安装

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

以上程序在每次重试之间以interval * backoff退避间隔尝试5次承诺流。

此外,如果您需要传递任何参数,则将其作为接受参数数组的args传递。将其包括在提到max_retriesintervalbackoff的选项部分。

这是官方文档https://www.npmjs.com/package/bluebird-retry