JavaScript | 非同期処理:設計・理解の深化 - 非同期関数の命名

JavaScript JavaScript
スポンサーリンク

なぜ「非同期関数の命名」がそんなに大事なのか

非同期処理って、それだけで頭を使います。
そこに「名前がふわっとしている関数」が混ざると、一気に読めなくなります。

doSomething(), handle(), run() みたいな名前だと、

何をするのか
いつ終わるのか
結果を返すのか返さないのか

が、コードを開いて中身を読まないと分かりません。

逆に言うと、
非同期関数の名前がよくできていると、「中身を開かなくてもだいたい分かる」状態に近づきます。
これは、非同期コードの可読性・保守性を一気に上げる超重要ポイントです。


非同期関数の名前で最低限伝えたいこと

「何をするか」と「何を返すか」

まず、非同期かどうか以前に、
関数名で伝えたいのはこの2つです。

何をする関数なのか
何を返す(または副作用を起こす)のか

例えば、次の二つを比べてみてください。

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

後者の方が圧倒的に読みやすいですよね。
「現在のユーザーを取得する非同期処理なんだな」と、一瞬で分かります。

ここで大事なのは、
「名前だけで“何をするか”がイメージできるか?」
という視点です。
非同期かどうかは、その次の話です。


「非同期らしさ」を名前でどう表現するか

fetch / load / request などの動詞をうまく使う

非同期処理は、だいたい「どこかにお願いして結果を待つ」タイプの仕事です。
なので、名前にもそのニュアンスを入れると分かりやすくなります。

よく使われるパターンは、例えばこんな感じです。

fetchUser
「どこか(サーバーなど)からユーザー情報を取ってくる」

loadUserProfile
「ユーザープロフィールを読み込む(画面表示のためのデータ取得を含むことが多い)」

requestPasswordReset
「パスワードリセットをサーバーに依頼する」

これらは、
「時間がかかるかもしれない」「外部とのやり取りがありそう」
という雰囲気を名前から感じ取れます。

重要なのは、
「同期関数と非同期関数で、動詞の使い方に一貫性を持たせる」
ことです。

例えば、

同期的にキャッシュから取る関数を getUserFromCache
非同期にサーバーから取る関数を fetchUserFromServer

のようにしておくと、
「これは待ちが発生しそうだな」という感覚が名前から伝わります。

Async を付けるかどうか問題

getUserAsync みたいに、
関数名の末尾に Async を付けるスタイルもよくあります。

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

これはこれで分かりやすいのですが、
最近の JavaScript では、

「非同期なら async であることはコードを見れば分かる」
fetchload などの動詞で非同期っぽさを表現する」

というスタイルも多いです。

どちらが正解、というよりは、
「プロジェクト内でルールを揃えること」が一番大事 です。

個人学習の段階なら、
「Promise を返す関数には Async を付ける」
「外部通信をする関数には fetch / request を使う」
など、自分ルールを決めておくと迷いにくくなります。


「結果を返す関数」と「副作用を起こす関数」を名前で分ける

値を返すなら get / fetch 系、UI を変えるなら show / render 系

非同期関数には、大きく分けて二種類あります。

何かの値(データ)を返す関数
何かの副作用(UI 更新・ログ送信など)を起こす関数

これをごちゃ混ぜにすると、
「この関数は await した結果を使うべきなのか?
それとも、呼ぶだけで仕事が終わるのか?」
が分かりにくくなります。

例えば、次の二つを比べてみてください。

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

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

前者は「データ取得」と「UI 更新」が一つの関数に混ざっています。
後者は、

fetchUser:値を返す非同期関数
renderUserPage:副作用(画面更新)を行う非同期関数

と役割が分かれています。

名前もそれに合わせて、

fetchUser → 「ユーザーを取ってくる」
renderUserPage → 「ユーザーのページを描画する」

と、やっていることが一言で説明できます。

ここが重要です。
「この非同期関数は、“何かを返すのか”“何かを起こすのか”を、名前で区別する。」
それだけで、呼び出し側のコードがかなり読みやすくなります。


「順番」と「並列」を名前で伝える

意図的な順次処理には sequential / inOrder などを使う

非同期処理では、
「並列にできるけど、あえて順番にやっている」
というケースがあります。

例えば、こういう関数。

async function processItems(items) {
  for (const item of items) {
    await processItem(item);
  }
}
JavaScript

これだけだと、
「並列にできるのに、ただ書き方を知らないだけなのか?」
「順番にやる意味があるのか?」
が読み手には分かりません。

意図を名前に乗せると、ぐっと分かりやすくなります。

async function processItemsSequentially(items) {
  // 順番が重要なので、あえて直列で処理する
  for (const item of items) {
    await processItem(item);
  }
}
JavaScript

逆に、並列で処理する関数なら、

async function processItemsInParallel(items) {
  const promises = items.map((item) => processItem(item));
  await Promise.all(promises);
}
JavaScript

のように、
InParallel などを付けておくと、
「これは並列に走るんだな」と一目で分かります。

ここが重要です。
「順番にやるのか、並列にやるのか」という設計上の意図を、名前で表現する。
読み手に“推測させない”ことが可読性につながる。


「非同期関数の命名」を意識したミニ例題

悪い例と、少し良くした例を比べる

まず、よくある「もったいない名前」の例から。

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

  const postsRes = await fetch(`/api/posts?userId=${user.id}`);
  const posts = await postsRes.json();

  render(user, posts);
}
JavaScript

doInit という名前からは、

何を初期化しているのか
何を返すのか
どんな非同期処理をしているのか

が全く分かりません。

これを、名前と責務を意識して書き直すと、こうなります。

async function fetchCurrentUser() {
  const res = await fetch("/api/user");
  if (!res.ok) throw new Error("ユーザー取得に失敗しました");
  return res.json();
}

async function fetchUserPosts(userId) {
  const res = await fetch(`/api/posts?userId=${userId}`);
  if (!res.ok) throw new Error("投稿取得に失敗しました");
  return res.json();
}

async function loadUserPageData() {
  const user = await fetchCurrentUser();
  const posts = await fetchUserPosts(user.id);
  return { user, posts };
}

async function renderUserPage() {
  try {
    const { user, posts } = await loadUserPageData();
    render(user, posts);
  } catch (err) {
    showErrorMessage(err.message);
  }
}
JavaScript

名前だけ見ても、

fetchCurrentUser → 現在のユーザーを取得する
fetchUserPosts → そのユーザーの投稿を取得する
loadUserPageData → ユーザーページに必要なデータをまとめて読み込む
renderUserPage → ユーザーページを描画する

という「物語」が見えます。

ここがポイントです。
「非同期関数の命名は、“処理の流れを日本語で説明している”くらいのつもりで付ける。」
そうすると、コード全体が自然な文章のように読めるようになっていきます。


初心者として「非同期関数の命名」で意識してほしいこと

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

この関数名だけ見て、「何をするか」がイメージできるか?
この関数は値を返すのか、副作用を起こすのか、名前から分かるか?
非同期っぽさ(外部通信・待ち時間)が名前ににじんでいるか?
順番にやるべき処理か、並列にできる処理か、その意図を名前で表現できているか?
同じプロジェクト内で、似た役割の関数の名前に一貫性があるか?

おすすめの練習は、
自分の書いた非同期関数の名前を一つずつ眺めて、
「これ、友達に口頭で説明するとしたら、どう言い換える?」
と考えてみることです。

その「口頭での説明」を、そのまま英語(または日本語)に落としたものが、
だいたい良い関数名になります。

非同期処理を「動かせる」だけじゃなく、
「名前だけで物語が伝わる非同期関数」を書けるようになったとき、
あなたのコードは一気に“プロっぽい顔つき”になります。

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