1. ホーム
  2. javascript

[解決済み] プロミス・リトライ・デザインパターン

2023-02-17 10:13:39

質問

編集

  1. プロミスが解決するまでリトライし続けるパターン( delaymaxRetries ).
  2. 結果が条件を満たすまでリトライし続けるパターン。 というパターンがあります。 delaymaxRetries ).
  3. 無制限に再試行できるメモリ効率の良いダイナミックパターン ( delay が提供されます)。

1のコードです。 promiseが解決するまでリトライし続ける(言語などの改善コミュニティは?)

Promise.retry = function(fn, times, delay) {
    return new Promise(function(resolve, reject){
        var error;
        var attempt = function() {
            if (times == 0) {
                reject(error);
            } else {
                fn().then(resolve)
                    .catch(function(e){
                        times--;
                        error = e;
                        setTimeout(function(){attempt()}, delay);
                    });
            }
        };
        attempt();
    });
};

使用する

work.getStatus()
    .then(function(result){ //retry, some glitch in the system
        return Promise.retry(work.unpublish.bind(work, result), 10, 2000);
    })
    .then(function(){console.log('done')})
    .catch(console.error);


2のコード が条件を満たすまでリトライし続けます。 then の結果を再利用可能な方法で再試行します(条件は様々です)。

work.publish()
    .then(function(result){
        return new Promise(function(resolve, reject){
            var intervalId = setInterval(function(){
                work.requestStatus(result).then(function(result2){
                    switch(result2.status) {
                        case "progress": break; //do nothing
                        case "success": clearInterval(intervalId); resolve(result2); break;
                        case "failure": clearInterval(intervalId); reject(result2); break;
                    }
                }).catch(function(error){clearInterval(intervalId); reject(error)});
            }, 1000);
        });
    })
    .then(function(){console.log('done')})
    .catch(console.error);

どのように解決するのですか?

ちょっと違うかな.

非同期リトライは .catch() チェーンを構築することで実現できます。 .then() チェーンとは異なります。

このやり方は.

  • 指定された最大試行回数のみ可能です。(チェーンは有限の長さである必要があります)。
  • は低い最大値でのみ推奨されます。(プロミス チェーンはその長さにほぼ比例してメモリを消費します)。

そうでなければ、再帰的な解決策を使用します。

まず、効用関数として .catch() のコールバックとして使用します。

var t = 500;

function rejectDelay(reason) {
    return new Promise(function(resolve, reject) {
        setTimeout(reject.bind(null, reason), t); 
    });
}

これで、非常に簡潔に .catch チェーンを構築することができます。

1. プロミスが解決するまでリトライする、遅延あり

var max = 5;
var p = Promise.reject();

for(var i=0; i<max; i++) {
    p = p.catch(attempt).catch(rejectDelay);
}
p = p.then(processResult).catch(errorHandler);

デモ : https://jsfiddle.net/duL0qjqe/

2. 結果が何らかの条件を満たすまで、遅滞なく再試行する。

var max = 5;
var p = Promise.reject();

for(var i=0; i<max; i++) {
    p = p.catch(attempt).then(test);
}
p = p.then(processResult).catch(errorHandler);

デモ : https://jsfiddle.net/duL0qjqe/1/

3. 結果が何らかの条件を満たすまで、遅延を伴って再試行する。

(1)と(2)を理解した上で、テストと遅延の組み合わせも同様に些細なことです。

var max = 5;
var p = Promise.reject();

for(var i=0; i<max; i++) {
    p = p.catch(attempt).then(test).catch(rejectDelay);
    // Don't be tempted to simplify this to `p.catch(attempt).then(test, rejectDelay)`. Test failures would not be caught.
}
p = p.then(processResult).catch(errorHandler);

test() は同期でも非同期でもかまいません。

さらにテストを追加するのも簡単でしょう。2つのキャッチの間にthensの連鎖を挟むだけです。

p = p.catch(attempt).then(test1).then(test2).then(test3).catch(rejectDelay);

デモ : https://jsfiddle.net/duL0qjqe/3/


すべてのバージョンは attempt はプロミスを返す非同期関数であるように設計されています。この場合、チェーンはその成功経路を辿って次/終端である .then() .