LoginSignup
0
0

More than 1 year has passed since last update.

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

Last updated at Posted at 2021-11-13

はじめに

はじめまして。この度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を取得する
  2. useridを用いて、/userinfoをたたき、各ユーザの詳細情報を取得する
  3. すべてが終わったら、元の順番に取得したユーザの情報を出す

解決策

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

準備

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

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はエラーになったら、どうなるのでしょう。

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

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操作関数を下記のようにしていきましょう:

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

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

// 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

0
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0