JavaScript | 非同期処理:パフォーマンス最適化 - 非同期設計レビュー

JavaScript JavaScript
スポンサーリンク

「非同期設計レビュー」って何をする時間か

非同期処理のコードは、とりあえず動かすだけなら意外と簡単です。
async/await を付けて、fetch を呼んで、console.log して終わり。

でも「速さ」「安定性」「UI の気持ちよさ」まで含めてちゃんと設計しようとすると、
一度立ち止まってコード全体を見直す時間が必要になります。

それが「非同期設計レビュー」です。

ここでは、
「どこをどう見ればいいか」を、チェックポイントと具体例で整理していきます。
実務のコードレビューで僕が必ず見る観点を、そのまま初心者向けにかみ砕きます。


依存関係の整理:「本当に順番にやる必要があるか?」

依存している処理と、していない処理を分けて考える

まず一番大事なのは、
「この非同期処理たちは、お互いに結果を必要としているか?」
という視点です。

例えば、次のようなコードがあったとします。

async function loadPage() {
  const user = await fetchUser();                 // 1
  const posts = await fetchPosts(user.id);        // 2
  const notifications = await fetchNotifications(user.id); // 3
  return { user, posts, notifications };
}
JavaScript

ここでは、
2 と 3 は user.id に依存していますが、
2 と 3 同士は依存していません。

つまり、
「1 は先にやる必要があるけど、2 と 3 は同時にやっていい」
という構造になっています。

これをレビューで見つけたら、
次のように書き換えを検討します。

async function loadPage() {
  const user = await fetchUser();

  const postsPromise = fetchPosts(user.id);
  const notificationsPromise = fetchNotifications(user.id);

  const [posts, notifications] = await Promise.all([
    postsPromise,
    notificationsPromise,
  ]);

  return { user, posts, notifications };
}
JavaScript

ここでのポイントは、
「依存関係があるところだけ順番を守り、それ以外は並列にする」
という設計にできているかどうかです。

レビューでは必ず、
「この await は“依存のため”に必要なのか、“なんとなく”置いてあるのか」
を問い直します。


await の位置と数:「本当にそこまで待つ必要があるか?」

「結果をすぐ使う await」と「ただの中継 await」を見分ける

次に見るのは、await の置き方です。

例えば、こんなコード。

async function getUser() {
  const user = await fetchUser();
  return user;
}
JavaScript

これは、こう書いても意味が同じです。

function getUser() {
  return fetchUser();
}
JavaScript

「中で何も加工せず、そのまま返しているだけ」の await は、
基本的に不要です。

レビューでは、
「この関数は本当に async である必要があるか?」
return await になっていないか?」
を必ず確認します。

もう一つ、よくあるパターンを見てみます。

async function loadData() {
  const a = await fetchA();
  const b = await fetchB();
  const c = await fetchC();
  return { a, b, c };
}
JavaScript

これも、依存関係がないならこう書けます。

async function loadData() {
  const aPromise = fetchA();
  const bPromise = fetchB();
  const cPromise = fetchC();

  const [a, b, c] = await Promise.all([aPromise, bPromise, cPromise]);
  return { a, b, c };
}
JavaScript

レビューでは、
「順番に await しているけど、本当に順番が必要か?」
「まとめて await できないか?」
を必ずチェックします。

ここが重要です。
await は「書けば安全」ではなく、「意味を持って置くスイッチ」です。
設計レビューでは、その一つ一つに意味があるかを確認します。


キャッシュとバッチ:「同じことを何度もしていないか?」

同じリクエストを何度も飛ばしていないか

非同期設計レビューでよく見るのが、
「同じ API を、画面のあちこちから何度も呼んでいる」パターンです。

例えば、ヘッダーとサイドバーとメインコンテンツで、
それぞれが fetchUser() を呼んでいるようなコード。

async function renderHeader() {
  const user = await fetchUser();
}

async function renderSidebar() {
  const user = await fetchUser();
}
JavaScript

こういうのを見つけたら、
「Promise キャッシュ」や「グローバルな getUser()」への抽象化を提案します。

let userPromise = null;

function getUser() {
  if (!userPromise) {
    userPromise = fetchUser();
  }
  return userPromise;
}
JavaScript

レビューの観点としては、
「同じデータを、同じセッション中に何度も取りに行っていないか?」
「キャッシュできる単位はないか?」
を必ず見ます。

バッチ処理でまとめられないか

ID ごとに API を叩いているコードも要注意です。

async function loadUsers(ids) {
  const users = [];
  for (const id of ids) {
    users.push(await fetchUser(id));
  }
  return users;
}
JavaScript

これを見たら、
「サーバー側にバッチ API を用意できないか?」
「クライアント側でバッチングできないか?」
という話をします。

非同期設計レビューでは、
「回数を減らす」「まとめる」という視点がとても重要です。
速さだけでなく、サーバー負荷やネットワーク効率にも効いてきます。


UI と非同期の関係:「ユーザー体験を壊していないか?」

長時間処理で UI をブロックしていないか

レビューで必ず見るのが、
「重い同期処理が紛れ込んでいないか」です。

例えば、async 関数の中に巨大な for ループがある場合。

async function processData(arr) {
  let sum = 0;
  for (const item of arr) {
    for (let i = 0; i < 100000; i++) {
      sum += item * i;
    }
  }
  return sum;
}
JavaScript

async と書いてあっても、
中身が同期なら普通に UI をブロックします。

ここでは、
「チャンクに分割して、間に休憩を挟せないか?」
「本当にメインスレッドでやるべき処理か?」
を問い直します。

ローディングとフィードバックが設計されているか

非同期処理は「待ち」が必ず発生します。
レビューでは、
「その待ち時間にユーザーは何を見ているか?」
も必ず確認します。

ローディング表示があるか
先に出せる情報だけでも表示しているか
楽観的 UI にできる部分はないか

コードだけでなく、
「この await の間、画面はどうなっている?」
という会話をするのが、非同期設計レビューの大事な部分です。


エラーとタイムアウト:「失敗したときの顔つきはどうなっているか?」

エラーがちゃんと伝播しているか

非同期処理は失敗します。
ネットワークエラー、タイムアウト、サーバーエラー…。

レビューでは、
「この await が失敗したらどうなる?」
を一つずつ追いかけます。

try/catch が必要なところにあるか
Promise を返すだけの関数で、エラーが握りつぶされていないか
Promise.all でどれか一つ失敗したときの挙動を理解しているか

特に、return await を使っている場合、
エラーのスタックトレースの見え方が変わることもあるので、
「本当にそれが必要か?」を確認します。

タイムアウトやリトライの設計があるか

長いレイテンシを前提にするなら、
「どこまで待つか」「失敗したらどうするか」も設計の一部です。

レビューでは、
「この API は永遠に待ち続ける設計になっていないか?」
「ユーザー操作に対して、許容できる待ち時間はどれくらいか?」
といった話もします。

非同期設計レビューは、
「成功パターンだけを見る時間」ではありません。
失敗や遅延も含めて、全体の振る舞いを確認する時間です。


計測と検証:「速そう」ではなく「速い」と言えるか?

実際に測るコードがどこかにあるか

最後に、
「この非同期設計は、本当に速くなっているのか?」
を確認するための計測があるかどうかを見ます。

console.time / console.timeEnd
performance.now() を使った簡易計測
計測用のラッパー関数

どんな形でもいいので、
「測ろうと思えばすぐ測れる状態」になっているかを確認します。

設計レビューでよくやるのは、
「この書き方と、もう一つの案で、実際に何回か測ってみよう」
という提案です。

感覚ではなく数字で話せるようになると、
非同期設計の議論の質が一気に上がります。


初心者として「非同期設計レビュー」で本当に持っていてほしい視点

最後に、あなたの頭の中に置いておいてほしい質問をまとめます。

この await は、何に依存しているから必要なのか?
この処理は、本当は並列にできないか?
同じことを何度もやっていないか?キャッシュやバッチにできないか?
この処理の間、ユーザーは何を見ている?UI は気持ちいいか?
失敗したらどうなる?どこまで待つ?
「速そう」じゃなくて「速い」と言えるだけの計測をしたか?

自分の書いた非同期コードを一つ選んで、
この質問たちを一つずつぶつけてみてください。

それを繰り返していくと、
あなたの中に「非同期設計レビュー脳」が育っていきます。

その脳が育ったとき、
あなたはもう「async/await を知っている人」ではなく、
本当に意味での
「非同期処理を設計できるエンジニア」になっています。

タイトルとURLをコピーしました