Promiseチェーンの中で、特定の条件を満たすまで同じ処理を繰り返したい場合があります。
例: データロード処理
そんな場合のコードサンプルです。
コード
こちら。
const retryPromise = (func, delay) => { const retry = (resolve, reject) => func() .then((result) => ({ result, isCompleted: (result !== null) })) .then(({ result, isCompleted }) => (isCompleted) ? resolve(result) : setTimeout(() => retry(resolve, reject), delay)) .catch(reject); return new Promise(retry); };
解説
解説をコメントで付け加えました。コード自体は上記のものと同じです。
// func: Promiseを返す関数, delay: リトライの時間間隔 const retryPromise = (func, delay) => { // Promiseを返す関数を実行 const retry = (resolve, reject) => func() // Promiseの結果そのものと終了条件をreturn .then((result) => ({ result, isCompleted: (result !== null) })) // 終了条件を満たしていたらresolveしてチェーンを抜ける // そうでなければ、delayミリ秒後に再実行 .then(({ result, isCompleted }) => (isCompleted) ? resolve(result) : setTimeout(() => retry(resolve, reject), delay)) .catch(reject); // retryPromise関数で最初に実行される部分 return new Promise(retry); };
パッと見では分かりづらいかもしれません。要は再帰関数的に処理を繰り返しています。
ポイントは再帰する時にreolve, reject
を渡している点です。
またリトライ処理と言えばsetInterval
関数が思いつきますが、clearInterval
しないといけないので面倒です。
例
リトライPromiseを使った例です。
// データfetchを模した関数 const fetchData = () => { // 低確率でデータのfetchに成功します if(Math.floor(Math.random() * 10) >= 9) { console.log('Fetch success!') return Promise.resolve({ data: 'foo' }) } else { console.log('Fetch fail...') return Promise.resolve(null) } } const retryPromise = (func, delay) => { const retry = (resolve, reject) => func() .then((result) => ({ result, isCompleted: (result !== null) })) .then(({ result, isCompleted }) => { if(isCompleted) { return resolve(result) } else { console.log(`Retry in ${delay}ms`) return setTimeout(() => retry(resolve, reject), delay) } }) .catch(reject); return new Promise(retry); }; retryPromise(fetchData, 1000) .then((result) => { console.log('Fetch completed!', result) })
実行結果がこちら。
Fetch fail... Retry in 1000ms Fetch fail... Retry in 1000ms Fetch fail... Retry in 1000ms Fetch fail... Retry in 1000ms Fetch fail... Retry in 1000ms Fetch fail... Retry in 1000ms Fetch success! Fetch completed! { data: 'foo' }
他に良い例がありましたらぜひ。