コンパイラかく語りき

import { Fun } from 'programming'

Promiseチェーンの中で条件を満たすまで同じ処理を繰り返す(リトライ処理)

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' }

他に良い例がありましたらぜひ。