問.以下のAPIにAjaxでリクエストを送り、レスポンスを表示しなさい（Promiseと再帰）

## 問題
以下のAPIにAjaxでGETリクエストを送り、レスポンスデータの "message" フィールドの値をコンソールに表示してください。

https://sheltered-island-72837.herokuapp.com/

なお、上記APIはそこそこの確率でエラーレスポンス（ステータスコード500）を返してきます。
もし、1回のリクエストで正常なレスポンス（ステータスコード200）が得られなかった場合は、再度APIへの問い合わせを行ってください。
その際、各リクエストは3秒以上の間を設けるようにし、また最大リトライ回数は2回とします。
リクエストにはaxiosなどのライブラリを使用して構いません。

## 実装例

```javascript
const axios = require('axios')
const url = 'https://sheltered-island-72837.herokuapp.com/'

const fetch = (executeCount = 1) => {
  return axios
    .get(url)
    .then(({data}) => data.message)
    .catch(({response}) => {
      if (executeCount < 3) {
        return new Promise(resolve => {
          setTimeout(() => {
            resolve(fetch(++executeCount))
          }, 3000)
        })
      }
      return response.data.message
    })
}

fetch().then(res => console.log(res))
```

## 解説
### APIへリクエストを送る
まずは単純に、APIに1回だけ問い合わせてコンソールに表示してみます。

Ajaxをする際、[axios](https://github.com/axios/axios)を利用することが多いと思います。 
もちろん、[Fetch API](https://developer.mozilla.org/ja/docs/Web/API/Fetch_API) を使ってもいいでしょう。 
今回は axios でいきます。

```javascript
const axios = require('axios')
const url = 'https://sheltered-island-72837.herokuapp.com/'

axios
  .get(url)
  .then(({data}) => console.log(data.message))
  .catch(({response}) => console.log(response.data.message))
```
{data} という書き方は、[分割代入 (Destructuring assignment)](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring) といいます。
オブジェクトや配列の一部だけを取り出して、別の変数に代入してくれます。

### リトライ機能
#### メソッド化
まず、先ほどの「APIへリクエストを送る」処理を再利用できるようにメソッドにします。
合わせて、thenやcatch内で直接console.logを呼んでいたのを、値を返すように修正します。

```javascript

const axios = require('axios')
const url = 'https://sheltered-island-72837.herokuapp.com/'

const fetch = () => {
  return axios
    .get(url)
    .then(({data}) => data.message)
    .catch(({response}) => response.data.message)
}

fetch().then(res => console.log(res))
```
thenやcatchのなかで値を返すとその値はPromiseでラップされる、ということはとても大事なので覚えておきましょう。
[Promise.prototype.then()
](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/then)

「値を返すように修正って、return書いてなくない...？」と思った方は、アロー関数の構文を復習しましょう。
[アロー関数](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/Arrow_functions)

#### 再帰
実現したいことは、「エラーになったら再度リクエストを送る」です。
「エラーになったら」なので、catchの中にその処理を書いていくことになります。
そして、「リクエストを送る」処理は、まさにfetchメソッド自身がそのための処理なのですから、自分を呼び出してしまえばいいわけです。

まず、fetchメソッドが実行回数を取るように引数を追加します。
そしてエラーになった時、実行された回数が上限（初回 + リトライ2回 = 3回）に達していなければ実行回数を+1して自分自身を呼び出します。
そうでなければ普通にレスポンスからメッセージを取り出して return します。

再帰を使うときはその終了条件をしっかり書いてあげないと、いつまでたっても自分を呼び出し続けて終わらなくなりますので十分意識してください。

なお、処理の流れを確認するため、一時的にconsole.logを追加しています。

```javascript
const axios = require('axios')
const url = 'https://sheltered-island-72837.herokuapp.com/'

const fetch = (executeCount = 1) => {
  console.log(`${executeCount}回目の試行です`)
  return axios
    .get(url)
    .then(({data}) => data.message)
    .catch(({response}) => {
      if (executeCount < 3) {
        console.log('失敗したので再度リクエストします')
        return fetch(++executeCount)
      }
      return response.data.message
    })
}

fetch().then(res => console.log(res))
```

### 間隔を開けてリクエストする
最後に、リトライ時、リクエストごとに間隔を開ける処理を実装しましょう。
リトライ時の挙動の調整ですから、再帰でfetchを呼び出している部分を修正します。

〜秒処理を待つ、といえば [setTimeout](https://developer.mozilla.org/ja/docs/Web/API/WindowTimers/setTimeout) ですね。

```javascript

console.log('a')

setTimeout(() => {
  console.log('b')
}, 1)

console.log('c')

//出力順
// a
// c
// b
```

では、setTimeout内で実行される関数から、返り値を受け取りたいときはどうすれば良いでしょう。

```javascript
const res = setTimeout(() => {
  return 'b'
}, 1)
```
これで res = 'b' となるでしょうか？ 違いますね。
setTimeoutは、指定した時間（第2引数）待って、第1引数に設定した処理を行う、というタイマーを設定する関数です。
setTimeoutの返り値は、そのとき設定されたタイマーのidです。

正解は、setTimeout全体をPromiseでラップして、返したい値でresolveすることです。

```javascript
const res = new Promise(resolve => {
  setTimeout(() => {
    resolve('b')
  }, 1)
})

res.then(data => console.log(data))
// b が出力される
```

したがって、修正は以下のようになります。
3秒待つので、setTimeout(() => {}, 3000)、setTimeout自体をPromiseでラップして、返したい値（fetch(++executeCount)） でresolveしています。

```javascript
const axios = require('axios')
const url = 'https://sheltered-island-72837.herokuapp.com/'

const fetch = (executeCount = 1) => {
  return axios
    .get(url)
    .then(({data}) => data.message)
    .catch(({response}) => {
      if (executeCount < 3) {
        return new Promise(resolve => {
          setTimeout(() => {
            resolve(fetch(++executeCount))
          }, 3000)
        })
      }
      return response.data.message
    })
}

fetch().then(res => console.log(res))
```

実際に外部のAPIを叩く時でも、エラーが返ってくるということはAPIに何かしら不具合・過負荷が起きている可能性がありますから、優しいリクエストを送ってあげることは大事です。

## よくわからなかったという人へ
解説と言いつつ全く詳細を説明していませんので、初心者の方にはちんぷんかんぷんだと思います。
（当たり前のように require とか使ってるし、Promiseって何なのかも説明していないし...）
でも、Javascriptを書いていく上で理解しておいた方がいいことばかりなので、勉強しましょう！
特にPromiseは大事です。フロントエンドでもサーバーサイドでも、Javascriptにおいて非同期処理の扱いはとっっても大事です。

https://sheltered-island-72837.herokuapp.com/

上記APIはしばらく稼働させておきますので、練習にお使いください。
（節度のあるリクエストをお願いします！）