JavaScript | 非同期処理:パフォーマンス最適化 - 非同期処理の計測

JavaScript JavaScript
スポンサーリンク

なぜ「非同期処理の計測」が大事なのか

パフォーマンス最適化って、つい「こうした方が速そう」と感覚でやりがちです。
でも、本当にやるべきことはシンプルで、たった一つです。

「遅いところをちゃんと測って、事実ベースで判断する」

非同期処理は待ち時間が見えにくいので、
計測しないと「どこでどれくらい時間がかかっているか」が分かりません。

だからこそ、
「測る → 見える → 直す」というサイクルを回せるようになることが、
非同期パフォーマンスの第一歩になります。


一番簡単な計測:console.time / console.timeEnd

基本の使い方

まずは、超手軽なやつからいきましょう。
console.timeconsole.timeEnd です。

console.time("fetch-user");

const res = await fetch("/api/user");
const user = await res.json();

console.timeEnd("fetch-user");
JavaScript

このコードを実行すると、コンソールにこんな感じで出ます。

fetch-user: 123 ms

ラベル(ここでは “fetch-user”)を付けておいて、
time で計測開始、timeEnd で終了、
その間にかかった時間を自動で出してくれます。

非同期処理でも、
await を挟んで問題なく使えます。

ここで重要なのは、
「なんとなく遅い気がする」ではなく、「この処理はだいたい〇〇msかかっている」と言えるようになること
です。

複数の処理を比べる

例えば、直列と並列の違いを測ってみましょう。

async function fetchA() {
  await new Promise((r) => setTimeout(r, 500));
}
async function fetchB() {
  await new Promise((r) => setTimeout(r, 700));
}

async function sequential() {
  console.time("sequential");
  await fetchA();
  await fetchB();
  console.timeEnd("sequential");
}

async function parallel() {
  console.time("parallel");
  await Promise.all([fetchA(), fetchB()]);
  console.timeEnd("parallel");
}

sequential().then(parallel);
JavaScript

これを実行すると、
sequential はだいたい 1200ms 前後、
parallel はだいたい 700ms 前後になるはずです。

数字で見ると、
「並列にする意味」が一気に腹落ちします。


もう少し細かく測りたいとき:performance.now()

ミリ秒単位で「今の時刻」を取る

performance.now() は、
「ページが開いてからの経過時間(ミリ秒)」を返してくれる関数です。

これを使うと、自分で好きなように計測できます。

const start = performance.now();

const res = await fetch("/api/user");
const user = await res.json();

const end = performance.now();
console.log("処理時間:", Math.round(end - start), "ms");
JavaScript

console.time とやっていることは同じですが、
startend の値を変数として持てるので、
ログ以外にも使えます。

例えば、
「一定時間以上かかったら警告を出す」
みたいなこともできます。

const start = performance.now();
await doSomethingAsync();
const end = performance.now();

const duration = end - start;
if (duration > 1000) {
  console.warn("1秒以上かかっている処理があります:", duration);
}
JavaScript

非同期関数を「計測ラッパー」で包む

計測のパターンを関数化する

毎回同じような計測コードを書くのは面倒なので、
「計測付きで実行する関数」を一つ用意しておくと便利です。

async function measure(label, fn) {
  const start = performance.now();
  const result = await fn();
  const end = performance.now();
  console.log(`${label}: ${Math.round(end - start)} ms`);
  return result;
}
JavaScript

使い方はこうです。

async function fetchUser() {
  const res = await fetch("/api/user");
  return res.json();
}

async function main() {
  const user = await measure("fetchUser", () => fetchUser());
  console.log(user);
}

main();
JavaScript

これで、
measure("fetchUser", ...) と書くだけで、
その処理にかかった時間が毎回ログに出ます。

ここが重要です。
「計測を簡単にする仕組み」を一つ持っておくと、
“とりあえず測ってみるか”が習慣になる。
習慣になった瞬間、パフォーマンスの感覚が一気に鋭くなる。


どこを測るべきか?という視点

「外側」だけでなく「中身」も分解して測る

例えば、こんな関数があるとします。

async function loadPage() {
  const res = await fetch("/api/page-data");
  const data = await res.json();
  render(data);
}
JavaScript

これを丸ごと測るのも大事ですが、
「どこが遅いのか」を知るには、
もう少し分解して測る必要があります。

async function loadPage() {
  const start = performance.now();

  console.time("fetch");
  const res = await fetch("/api/page-data");
  console.timeEnd("fetch");

  console.time("json");
  const data = await res.json();
  console.timeEnd("json");

  console.time("render");
  render(data);
  console.timeEnd("render");

  const end = performance.now();
  console.log("total:", Math.round(end - start), "ms");
}
JavaScript

これで、

fetch が遅いのか
JSON パースが重いのか
render が重いのか

が数字で見えるようになります。

ここが重要です。
「遅い」と感じたら、“どこがどれくらい遅いのか”を分解して測る。
“なんとなく全部遅い”は、直しようがない。


計測するときに気をつけたい落とし穴

一回だけの計測を信じすぎない

ネットワークや CPU の状況は、
タイミングによってブレます。

同じ処理でも、
あるときは 120ms、
別のときは 200ms かかることも普通です。

だから、
一回だけ測って「この処理は 120ms」と決めつけるのは危険です。

簡単なやり方でいいので、
何回か繰り返して平均を取る、くらいはしておくと安心です。

async function measureMany(label, fn, times = 5) {
  let total = 0;
  for (let i = 0; i < times; i++) {
    const start = performance.now();
    await fn();
    const end = performance.now();
    total += end - start;
  }
  console.log(`${label} 平均: ${Math.round(total / times)} ms`);
}
JavaScript

これで、
「だいたいこのくらい」という感覚が、
少しだけ信頼できる数字になります。

計測コード自体が邪魔をしないようにする

ログを出しすぎると、
それ自体がパフォーマンスに影響することがあります。

開発中はガンガン計測していいですが、
本番環境では、

計測をオフにするフラグを用意する
一定の条件のときだけ計測する

といった工夫をしておくと安全です。

「計測のためのコードが、本番でユーザーを遅くしている」
というのは、本末転倒ですからね。


「計測できる人」になるためのマインドセット

最後に、技術というより考え方の話をします。

非同期処理のパフォーマンス最適化で一番強い人は、
「速そうな書き方を知っている人」ではなく、
「ちゃんと測ってから判断する人」 です。

何かを変えたら、
変える前と後で時間を測ってみる。
「本当に速くなったのか?」を数字で確認する。

そして、
「どこがボトルネックなのか?」を
感覚ではなく計測で特定する。

おすすめの練習は、
自分の書いた非同期処理を一つ選んで、
次の三つをやってみることです。

1つ目:console.time / console.timeEnd でざっくり測る。
2つ目:performance.now() で細かく分解して測る。
3つ目:少し書き方を変えて、もう一度測って比べる。

その繰り返しの中で、
「コードの書き方」と「数字」が頭の中で結びついていきます。

その感覚が育ってくると、
あなたはもう「なんとなく async/await を書く人」ではなく、
「非同期処理を設計し、数字で語れるエンジニア」 に近づいていきます。

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