Help us understand the problem. What is going on with this article?

async/await で複数の非同期処理を待つときに注意したいこと

More than 1 year has passed since last update.

本投稿のサンプルコードを GitHub に用意しました。
im36-123/multiple_await | GitHub

概要

async/await を使うとで非同期処理を同期処理のように記述できますが、複数の非同期処理を待つときに気をつけたいことを紹介します。
複数のリクエスト送信して、それらのレスポンスからページを構成するときなどに役立つかもしれません。

今回は Fetch API を使って複数のリクエストを送信する場合を考えてみます。

普通に async/await を使う

まずは、ひとつだけリクエストを送信する場合を考えてみます。

ひとつだけリクエストを送信する
function get(url) {
  return fetch(url);
}

async function fn() {
  const res = await get(`https://hoge/api`);
  console.log(res);
}

fn();

fn() が実行されると、 https://hoge/api へリクエストを送信します。
レスポンスがあるまで、 fn() 内の処理は停止します。
特に問題ありませんね。

次は本題の複数のリクエストを待つ場合を見てみましょう

複数のリクエストを待つ

それでは2つ以上のリクエストを送信するときを考えてみます。

良くない書き方

先程のコードのリクエストを送信する部分を2つにしました(実際は for文などで loop させるかと思います。)。

良くないawait
function get(url) {
  return fetch(url);
}

async function fn() {
  const res1 = await get(`https://hoge/api`);
  const res2 = await get(`https://fuga/api`);
  console.log(res1);
  console.log(res2);
}

fn();

こちらのコードは問題なく動作しますが、パフォーマンスが落ちてしまいます。
というのも、res1 に結果が代入されるまで fn() 内の処理が止まってしまうためです。

developer tool の Network タブを見てみると、確かに1つ目のリクエスト結果の後に、2つ目のリクエストが送信されています。
image.png
(※ 上図では Qiita の api にリクエストを送信しています。)

良い書き方

res1 の結果が res2 のリクエストに影響しない場合は、次のように複数のリクエストが並列して処理されるようにします。

先程のコードを修正して、2つのリクエストが並列して処理されるようにしました。
また、 Promise.all を使ってすべてのリクエスト結果の返却を待ちましょう。

良いawait
function get(url) {
  return fetch(url);
}

async function fn() {
  const results = [];
  const urls = ['https://hoge/api', 'https://fuga/api'];
  for (const url of urls) {
    results.push(get(url));
  }
  console.log(await Promise.all(results));
}

fn();

こちらも developer tool の Network タブから確認してみましょう。
image.png

今回は、2つのリクエストが並行して処理されているのがわかります。

まとめ

業務で async/await を使っているときに ESLint に注意されたので書きました。

追記

@41semicolon さんよりコメントをいただきました…!!
fn() に関してですがコメントで頂いた下記のコードの方がスマートで素敵です。

async function fn() {
  const urls = ['https://hoge/api', 'https://fuga/api'];
  console.log(await Promise.all(urls.map(get)));
}

呼び出し側で await するなどのときはこちら。

fn = (urls) => Promise.all(urls.map(get))

詳しくはコメントを参照お願いします。

参考

no-await-in-loop - Rules - ESLint - Pluggable JavaScript linter
async function - JavaScript | MDN
await - JavaScript | MDN

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away