web-dev-qa-db-ja.com

約束:成功するまで操作を繰り返しますか?

操作が成功するか、一定の時間が経過するまで、操作ごとにタイムアウトを増やしながら操作を繰り返し実行したい。 Qで約束を使ってこれをどのように構成しますか?

24
Jay Bienvenu

ここでのすべての答えは私の意見では本当に複雑です。 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に組み込まれているため、再発明する必要はありません:)

25

これは、いくつかのヘルパー関数を使用して、これに私がどのようにアプローチするかの例です。 2つの状態を競合させる必要があるため、「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 */);
4
loganfsmyth

私はあなたがそれをプロミスレベルで行うことはできないと思います-プロミスは操作ではなく、将来到着する単なる値なので、達成できるPromise -> Promiseと型指定された関数を定義することはできませんそれ。

1レベル下に移動して、Operation -> Promiseと入力された関数を定義する必要があります。ここで、Operation自体は() -> Promiseと入力されています。この操作はパラメーターをとらないと思います。事前に部分的に適用できます。

以下は、実行ごとにタイムアウトを2倍にする再帰的なアプローチです。

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/

2
Kos
  1. 「すべてのプロセスタイムアウト」にブール変数を割り当てます。
  2. ウィンドウのsetTimeoutを呼び出して、その「すべてのプロセスタイムアウト」の後でその変数を「false」にします。
  3. タイムアウト付きでpromiseオペレーションを呼び出します。
  4. 成功した場合は問題ありません。
  5. 失敗した場合:promiseのエラーハンドラーで、ブール変数がtrueの場合、タイムアウトを延長して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);

    }
})();
0
Kursad Gulseven

私は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();
})();

最大タイムアウト後に保釈する方法を理解できると思います。

0
spender