Promise:重复操作直到成功?

IT技术 javascript promise q
2021-03-02 04:22:17

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

6个回答

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

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

当然是有办法的。我用 maxAttempts 和一个计数器很容易地解决了它,并完全跳过了超时。我更担心的是,上面的答案在这方面存在缺陷,如果不小心,人们最终可能会产生无限操作,随着时间的推移消耗大量 CPU。
2021-04-23 04:22:17
老实说 - 超时很好 - 问题是首先使用异常进行流量控制 - 不使用异常进行流量控制而是返回结果更有意义 - 如果该结果不成功则终止操作 -这样超时可以让您轻松地从内部引发异常以获得您期望的行为。也就是说 - 随时发布替代答案。
2021-05-02 04:22:17
@JoãoPimentelFerreira Q 是一个图书馆。您可以替换Q.delay(N)new Promise(r => setTimeout(r, N))和活得很幸福:)
2021-05-04 04:22:17
这真是一个很棒的建议。但是,我发现了它的一个问题。即使超时到期,重试函数将继续调用自己直到成功,这显然可以是无限的。当超时发生时,是否有一种干净的方法来打破Promise链,以便不再调用更多的处理程序,或者我们是否不得不设置一个标志,或者甚至放弃 timeout() 以支持每次调用时递增的 nrAttempts 计数器operation() 并检查某些限制?
2021-05-05 04:22:17
@JHH 如果你让我使用 bluebird,我会使用取消 - 否则你会使用一个特殊的闭包变量来模拟它 - 或者将“令牌”(变量)传递到重试中并将其设置为 false。所以有很多方法,但没有真正干净的 Q 方法。
2021-05-05 04:22:17

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

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

您需要向下一层并定义一个 typed 函数Operation -> Promise,其中 Operation 本身是 typed () -> 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/

为什么延期?您可以使用Promise链。
2021-04-28 04:22:17
好吧,我根据 Petka 的(链接那里)写了一个关于这个特定问题的问答
2021-05-02 04:22:17
酷,谢谢你给我看!仍在学习这些,我总是不够直截了当。(还没有在小提琴之外尝试它们)
2021-05-03 04:22:17
  1. 为“所有进程超时”分配一个布尔变量。
  2. 在“所有进程超时”之后调用窗口的 setTimeout 使该变量为“false”。
  3. 使用超时调用 promise 操作。
  4. 如果成功就没有问题。
  5. 如果失败:在 promise 的错误处理程序中,如果布尔变量为真,则再次调用 promise 函数并增加超时时间。

像这样的东西:

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

    }
})();
我认为您可能.timeout对它的作用感到困惑(它实际上goOn只是在库中内置了您使用的功能)。
2021-05-04 04:22:17

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

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

你是想打电话a吗?
2021-04-30 04:22:17
@BenjaminGruenbaum 是的。很抱歉遗漏了。
2021-05-11 04:22:17