axiosでget後に特定条件時にリトライするように実装してみた

APIから情報取得してリソースの情報次第で再取得したいことってたまにありますよね？
バックエンド
でワーカーが走ってて処理待ちとか。そんなときに使えそうなリトライ処理を実装してみました。

## 環境準備

今回はnode.jsで動作するサンプルにしました。ブラウザ上でも実装は可能です。
node、yarn (or npm)がインストールされている前提です。

```sh
> node --version
v10.11.0
> yarn --version
1.12.3
> npm --version
6.4.1

> mkdir 任意のディレクトリ
> cd 任意のディレクトリ
> yarn init
> yarn add axios retryx
```

## 実装

実装はGitHubにもアップしていますので、よければご参考ください。
https://github.com/kai-kou/node-js-axios-retry

### リトライしない実装

リトライ処理を組み込んでいない状態の実装です。
Qiita API v2を利用させていただき、ユーザー情報を取得しています。

```js:リトライしない実装
noRetry = () => {
  const axiosBase = require('axios');
  const axios = axiosBase.create({
    baseURL: 'https://qiita.com/api/v2/',
  });

  axios.get('users/kai_kou')
  .then(function(res) {
    console.log(res.data);
  });
};

noRetry();
```

### Promiseを利用して頑張る

上記実装にリトライ処理を組み込みます。

リトライする条件は、記事数```items_count``` が```108``` 以下だったらリトライするようにしました。~~厳しい。~~


とりあえずライブラリなどを利用せず、自力で頑張ってみます。
といいつつ、下記を参考にさせていただきました。

Promiseチェーンの中で条件を満たすまで同じ処理を繰り返す（リトライ処理） - コンパイラかく語りき
http://chuckwebtips.hatenablog.com/entry/2017/07/17/081105


```js:Promiseを利用して頑張るやつ
retry = () => {
  const axios = require('axios');
  const client = axios.create({
    baseURL: 'https://qiita.com/api/v2/',
  });

  const retryPromise = (func, delay) => {
    const retry = (resolve, reject) => func()
    .then((result) => ({result, isCompleted: (result.items_count >= 108)}))
    .then((data) => {
      if (data.isCompleted) {
        resolve(data.result);
      }
      else {
        console.log('retry...');
        setTimeout(() => retry(resolve, reject), delay);
      }
    })
    .catch(reject);
    return new Promise(retry);
  };

  const func = () => {
    return client.get('users/kai_kou')
    .then((res) => {
      return Promise.resolve(res.data);
    });
  };

  retryPromise(func, 1000)
  .then((result) => {
    console.log(result);
  });
};

retry();
```

実装の詳細は上記記事をご参考ください。

```retryPromise``` メソッドで再帰的に終了判定```result.items_count >= 108``` が満たされるまで、処理を繰り返しています。
リトライ回数や待機時間にexponential backoffアルゴリズムの実装などを組み込んでいないため、実用するならば、もう少し頑張らないとだめです。

AWSユーザーは必ず覚えておきたいExponential Backoffアルゴリズムとは何か - yoshidashingo
https://yoshidashingo.hatenablog.com/entry/2014/08/17/135017


### ライブラリを利用する

`get`した場合に、ネットワークエラーや`5xx`エラーが発生してリトライしたい場合には下記のライブラリが利用できます。

JustinBeckwith/retry-axios: 🦖 A super flexible interceptor for Axios that makes it easy to retry requests.
https://github.com/JustinBeckwith/retry-axios

softonic/axios-retry: Axios plugin that intercepts failed requests and retries them whenever possible
https://github.com/softonic/axios-retry

ざっくり使ってみたところ、正常取得(2xx)時に自前でのリトライ判定を差し込むことはできませんでしたので、今回は下記ライブラリを利用させていただきました。

y13i/retryx: Promise-based retry workflow library.
https://github.com/y13i/retryx

よくよくみたら同じ部署の御方が作成されていました^^感謝！

Promise をリトライする何かを作った - Qiita
https://qiita.com/y13i/items/6ba3c8849c8af80d2dde

では実装です。

```js:ライブラリを利用した実装
retry = () => {
  const axios = require('axios');
  const retryx = require('retryx');

  const client = axios.create({
    baseURL: 'https://qiita.com/api/v2/',
  });

  retryx(() => client.get('users/kai_kou')
    .then((res) => {
      if (res.data.items_count < 108) {
        return Promise.reject(Error('記事が少ないです'));
      }

      return Promise.resolve(res.data);
    }),
    {
      beforeWait: (tries) => {
        console.log('beforeWait');
        console.log(tries);
      },
    })
  .then((res) => {
    console.log(res);
  });
};

retry();
```

うーん。シンプルで素敵です。ネットワークエラーや`5xx` エラー時にもリトライされます。
試行回数や待機時間の指定、リトライを継続するかの判定などのオプションも豊富でいい感じに使えそうです。


## まとめ

当初は、retry-axiosでなんとかなるかと考えて検証していたのですが、思いもかけずハマッてしまいましたが、探せばやっぱりあるものです。助かりました^^


## 参考

Promiseチェーンの中で条件を満たすまで同じ処理を繰り返す（リトライ処理） - コンパイラかく語りき
http://chuckwebtips.hatenablog.com/entry/2017/07/17/081105

AWSユーザーは必ず覚えておきたいExponential Backoffアルゴリズムとは何か - yoshidashingo
https://yoshidashingo.hatenablog.com/entry/2014/08/17/135017

JustinBeckwith/retry-axios: 🦖 A super flexible interceptor for Axios that makes it easy to retry requests.
https://github.com/JustinBeckwith/retry-axios

softonic/axios-retry: Axios plugin that intercepts failed requests and retries them whenever possible
https://github.com/softonic/axios-retry

y13i/retryx: Promise-based retry workflow library.
https://github.com/y13i/retryx

Promise をリトライする何かを作った - Qiita
https://qiita.com/y13i/items/6ba3c8849c8af80d2dde