[Javascript]Promiseを用いてネスト構造でAPI呼び出す方法

#はじめに
はじめまして。この度Qiitaを使い始めました。
Qiitaを通じて技術者の皆さんと交流し、新しい知識を勉強したいと思います。
さて、突然ですが、この間初めて知った「How to」をご紹介いたします。

#結論は最初に出す
ネスト構造（ループ）でAPIを呼び出すときに、`Promise.All`がとても役に立ちます。
ただし、各`promise`のエラーを慎重に扱うように。

#課題
以下のようなAPIで考えましょう。

```
Endpoint: /users
Method: GET
Query param: -

Response body:
{
  "users": [{
    "username": "James Bond",
    "userid": 007
  }]
}
```
```
Endpoint: /userinfo
Method: GET
Query param: userid

Response body:
{
  "userid": 007
  "username": "James Bond",
  "email": "jamesbond@secret.com",
  "registration_date": "1962-10-05",
  "nationality": "UK"
}
```
この二つのAPIに対して、以下のことを行います。

1. `/users`をたたき、すべてのユーザIDを取得する
1. `userid`を用いて、`/userinfo`をたたき、各ユーザの詳細情報を取得する
1. すべてが終わったら、元の順番に取得したユーザの情報を出す

#解決策
非同期処理でAPIを呼び出すとき、`Promise.all`を用いれば、すべてのAPI操作が終わるとき、次に進むのはできます。
では、`Promise.all`の使い方は何でしょうか？

###準備
下記の例を見てみましょう。

```javascript
const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, '1st');
});
const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, '2nd');
});
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 300, '3rd');
});

Promise.all([promise1, promise2, promise3])
.then((values) => {
  console.log(values);
});
// expected output: Array ["1st", "2nd", "3rd"]
```
ご覧の通り、最後の結果は、各`promise`の完成時間と関係なく、元通りですね。
ただし、`promise`はエラーになったら、どうなるのでしょう。

上の例を少し修正します：

```javascript
const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, '1st');
});
const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, '2nd');
});
const promise3 = new Promise((resolve, reject) => {
  setTimeout(reject, 300, '3rd'); // resolve → rejectにして、エラーを投げ出す
});

Promise.all([promise1, promise2, promise3])
.then((values) => {
  console.log(values);
})
.catch((error) => {
  console.log(error); // エラーもコンソールに出す
});
// expected output: "3rd"
```
ご覧の通り、エラーが出るとき、そのエラーだけが結果に出します。
`Promise.all`を用いて、すべての結果を取得したい時、各`promise`がエラーを出ないよう、コードを書かなければなりませんね。

###本題
では、課題に戻りましょう。
まずは前提として、API操作関数を下記のようにしていきましょう：

```javascript
await callApi(param?)
.then((result) => {
  // 成功したときの操作
})
.catch((error) => {
  // 失敗したときの操作
});
```

ここで、`promise`を活用しましょう。
###例

```javascript
// UserInfoJsonとは返したユーザ情報データです
// 場合によって別途定義するのは必要かも
const handleUserInfo = async (userid): Promise<UserInfoJson> => {
  let userInfo = {
    userid: null,
    username: null,
    email: null,
    registration_date: null,
    nationality: null
  };
  await getUserInfo(userid)
  .then((result) => {
    userInfo = result;
  })
  .catch((error) => {
    console.error(error);
  });
  return(userInfo);
};

await getUsers() // まずはユーザIDを取る
.then((result) => {
  let promises = [];
  result.users.map((item, index) => {
    promises.push(handleUserInfo(item.userid));
  });
  Promise.all(promises)
  .then((results) => {
    // resultsの処理
  });
})
.catch((error) => {
  // エラーの処理
});
```
`results`にはすべてのユーザ情報が元の順番に入っているので、ちゃんと利用できますね。

#最後に
この記事はあくまでも私個人（初心者として）の経験です。間違っているところ、もっと良い方法がございましたら、ぜひ教えてください。

#参考資料
https://medium.com/@guillermo_26794/asynchronous-fetchs-inside-a-loop-in-react-native-9210058e4b0
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all