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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26694467/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 06:19:35  来源:igfitidea点击:

Promises: Repeat operation until it succeeds?

javascriptpromiseq

提问by Jay Bienvenu

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?

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

回答by Benjamin Gruenbaum

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:

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

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:

如果您想在一段时间后超时(假设 10 秒,您可以简单地执行以下操作:

var promise = retry(operation, 1000).timeout(10000);

That functionality is built right into Q, no need to reinvent it :)

该功能内置于 Q 中,无需重新发明它:)

回答by loganfsmyth

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.

这是我如何使用一些辅助函数来解决这个问题的示例。请注意,“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]);
}

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 */);

回答by Kos

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 -> Promisethat will achieve it.

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

You'd need to go one level down and define a function typed Operation -> Promisewhere Operation is itself typed () -> Promise. I assume the operation doesn't take any parameters - you can partially-apply them beforehand.

您需要向下一层并定义一个 typed 函数Operation -> Promise,其中 Operation 本身是 typed () -> Promise。我假设该操作不采用任何参数 - 您可以事先部分应用它们。

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/

演示:http: //jsfiddle.net/0dmzt53p/

回答by spender

I did the following with Promises/A+ (which should be fine with Q)

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

I'm sure you could figure out how to bail after a maximum timeout.

我相信你可以弄清楚如何在最大超时后保释。

回答by Kursad Gulseven

  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.
  1. 为“所有进程超时”分配一个布尔变量。
  2. 在“所有进程超时”之后调用窗口的 setTimeout 使该变量为“false”。
  3. 使用超时调用 promise 操作。
  4. 如果成功就没有问题。
  5. 如果失败:在 promise 的错误处理程序中,如果布尔变量为真,则再次调用 promise 函数并增加超时时间。

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

    }
})();