JavaScriptではSleepに相当する処理が無いので気軽にリトライができない。async/awaitがあれば比較的簡単に解決できるが、IEにそのような機能は無い。
Promiseを使うことで解決できる。PromiseであればpolyfillがあるためIEでも利用できる。
ただ、いざ書いてみると大変面倒だったためメモしておく。
function retry(fn, opt){
opt = opt || {};
opt.times = opt.times || 3;
opt.delay = opt.delay || function() { return 1000 }; // ExpornentialBackoff したいとき
opt.abort = opt.abort || function() { return false }; // 中断したいときは true を返す
opt.count = 0;
opt.error = [];
// 初回は即実行して欲しい
var promise = new Promise(function(resolve,reject){
var result;
try{
result = fn()
resolve(result);
}catch(e){
opt.count++;
opt.error.push(e);
reject(opt);
}
});
var i = 0;
for(; i < opt.times; i++){
promise = promise.catch(function(opt){
// リトライは間隔を開けて欲しい
return new Promise(function(resolve,reject){
setTimeout(function(){
var result;
try {
if (opt.abort()) { return }
result = fn();
resolve(result);
}catch(e){
opt.count++;
opt.error.push(e);
reject(opt);
}
}, opt.delay());
});
});
}
return promise;
}
以下の必ず例外を発生する関数を retry
に投入すると、コンソールに1秒間隔で日付を6回(1+5回)表示し、最後に "fail" を表示する。
function fail() {
console.log(new Date());
throw 1;
}
retry(fail, { times: 5 }).catch(function(e){ console.log("fail") })
一方で、例外を発生しない場合は日付が1回表示されて終了する。
function pass() {
console.log(new Date());
}
retry(pass, { times: 5 }).catch(function(e){ console.log("fail") })
更に、以下は途中で正常終了するため、コンソールには 1, 2, 3, pass が表示されて終了する。
var i = 0;
function safe() {
i++;
console.log(i);
if (i < 3) { throw 1 }
}
retry(safe, { times: 5 }).then(function(){ console.log("pass") }).catch(function(e){ console.log("fail") })
以上