【JavaScript入門講座】 アロー関数と無名関数を使った非同期処理の基本:sleep関数を自作してみよう

はじめに

連日、ゲーム制作を進めていく中で、もはや当たり前となった「非同期処理」。

実践の中で学ぶ方が得られるものが多いですが、それでも基礎が分かっていないと後々困るのも良く知ってる為、 JavaScript (ES6以降) で導入された、関数を定義する簡潔な構文である、アロー関数。
更に無名関数など。

それらを踏まえてJavaScriptには何故かない、sleep関数の自作をしつつ再学習して見ます。

AI指導のもとの入門講座で、個人的な備忘録メモ的意味合いが強い記事ですが、誰かのお役たてば幸いです。

1. delay() の動作概要

JavaScriptでは、一定時間だけ処理を止めたい(いわゆる「スリープ処理」) という場面がよくあります。
たとえば、以下のような処理:

console.log("3秒後にメッセージを表示します");
await delay(3000); // ← 一時停止したい
console.log("こんにちは!");

ですが、JavaScriptには sleep() のような標準関数がありません。
その代わりに、次のような自作関数をよく使います。

const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

この1行で「一定時間だけ待つ Promise を返す関数」が完成します。

実行例:

async function test() {
  console.log("処理開始");
  await delay(2000); // 2秒待機
  console.log("2秒後に表示されるメッセージ");
}
test();

実行結果:

処理開始
(2秒後)
2秒後に表示されるメッセージ

✅ 結論:

delay(ms) は、await と組み合わせて「処理の一時停止」を表現できる、
JavaScriptにおける“sleep関数的なもの” です。

このあとで登場する アロー関数 や 無名関数、Promise の仕組みを理解すると、
この delay() の構造がすべて「見える化」されてきます。

2.アロー関数とは?

delay() を見てみると、こんな形が使われています:

const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

ここで使われている =>(イコール大なり)こそが、アロー関数です。

🔰 アロー関数とは?

アロー関数(Arrow Function)は、ES6(ECMAScript 2015) から導入された 関数の簡略記法 です。

従来の関数は次のように書いていました:

function greet(name) {
  return "こんにちは、" + name + "さん";
}

アロー関数にすると、同じ意味の処理がこう書けます:

const greet = (name) => {
  return "こんにちは、" + name + "さん";
};

さらに、処理が1行だけでreturnを含む場合は、波かっこもreturnも省略できます:

const greet = (name) => "こんにちは、" + name + "さん";

🧠 delay関数に当てはめると…

const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

これは実質、こういう意味:

function delay(ms) {
  return new Promise(function(resolve) {
    setTimeout(resolve, ms);
  });
}

つまり:

書き方 内容
(ms) => ... msを受け取って処理する関数
const delay 関数に名前をつけて変数に代入

アロー関数のメリット

  • コードが短く、読みやすい
  • 無名関数やコールバックとの相性が良い(後述)
  • this の挙動が分かりやすくなる(今回は省略)

ただし注意点も

  • this の扱いが従来の関数と違う(クラス・DOM操作では要注意)
  • arguments が使えない(関数に渡されたすべての引数を取得できない)

結論

アロー関数は、短く書ける“スマートな関数の形”
delay() は「アロー関数×Promise×無名関数」の組み合わせでできている

user icon

const greet = (name) => “こんにちは、” + name + “さん”;

これは凄いですね。 仕組みが分かってないと何をやってるのか全く分からない…。


ai icon
まさに、そうなんです! これって**“一見スマートに見えて、実は理解の壁になりやすい”**構文の代表なんですよね。

🧠 なぜ分かりづらいのか?

const greet = (name) => "こんにちは、" + name + "さん";

一見「ただの代入式?」に見えますが、実は:

  • greet という関数を作っていて
  • name という引数を受け取り
  • その値に応じた文字列を返している

という、関数定義+ロジック+return が全部詰まってるんです。

🧩 実はこう展開される

書き換えるとこう:

function greet(name) {
  return "こんにちは、" + name + "さん";
}

もしくは:

const greet = function(name) {
  return "こんにちは、" + name + "さん";
};

🤯 初心者が混乱しやすい理由

  • => が新しすぎて「演算子?」と思ってしまう
  • return が書かれていないのに「値を返している」のが分からない
  • const で変数を作ってるように見えて、実は関数

✅ でも一度理解できると…

  • 処理が1行ならどんどんスマートに書ける
  • コールバック処理(非同期や配列)で爆速コーディングができる
  • map(), filter(), setTimeout() などの中で真価を発揮する

3. 無名関数とは?

無名関数(anonymous function) とは、
その名の通り「名前のついていない関数」のことです。

通常の関数はこうやって名前をつけますよね:

function sayHello() {
  console.log("こんにちは");
}

これは「sayHello という名前の関数」です。
一方、無名関数はこんな形です:

function() {
  console.log("こんにちは");
}

↑ 名前がありません。
このような関数は、「その場限り」で使いたい時にとても便利です。

無名関数の使い方

無名関数はよく「コールバック関数」として使われます。

setTimeout(function() {
  console.log("3秒後に実行されました");
}, 3000);

上記の function() { … } が 無名関数です。 そして setTimeout() に渡される引数の一部として「その場で定義されて」います。

🧠 delay関数の中にも無名関数がある!

再掲すると、こうですね:

const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

この中の構造を分解すると:

new Promise(
  function(resolve) {
    setTimeout(function() {
      resolve(); // 時間が経ったら完了
    }, ms);
  }
);

この中には2つの無名関数が登場しています:

  1. function(resolve) { … } ← Promiseの中身
  2. function() { resolve(); } ← setTimeoutの中身

これがアロー関数で書かれているため、コンパクトになってこうなっています:

(ms) => new Promise((resolve) => setTimeout(resolve, ms))

無名関数の利点

利点 説明
① 一時的な処理に使える 名前をつけるほどではない処理に便利
② 名前の衝突を防げる グローバルに影響しない
③ コールバック関数との相性◎ 非同期処理やタイマー処理で多用される

❗ 注意点

  • 無名なので再利用できない(同じ処理を何度も書くことに)
  • デバッグ時にスタックトレースで関数名が出ない(追跡しにくい)

結論

無名関数は、「今この場だけ使いたい関数」をコンパクトに書ける便利な仕組み。
delay() の中で resolve => setTimeout(…) として使われているのは、まさにその典型!

user icon

注意点 デバッグ時にスタックトレースで関数名が出ない(追跡しにくい)

便利な反面、癖もあるんですね。


ai icon

まさにそうなんです……!

「便利=魔法のようなもの」だけど、魔法には“癖”や“リスク”もある、という感じですね🧙‍♂️✨


無名関数の“影”の部分

❗ 1. デバッグ時に出てこない名前

たとえば、こんなコードでエラーが起きた場合:

const doSomething = () => {
  setTimeout(() => {
    throw new Error("何かがおかしい!");
  }, 1000);
};

doSomething();

この場合、コンソールのエラーログでは:

Uncaught Error: 何かがおかしい
    at <anonymous>:1:23

のように、「どの関数内で起きたか分からない」 という問題が起こります。

✅ 対策の一例:名前付き関数式

const doSomething = () => {
  setTimeout(function throwErrorInTimeout() {
    throw new Error("何かがおかしい!");
  }, 1000);
};

↑ こうやって名前付きで書いておくと、スタックトレースがこうなります:

Error: 何かがおかしい
    at throwErrorInTimeout (...省略)

🧠 どこで無名関数を使い、どこで名前を付けるべきか?

状況 推奨
1回しか使わない / すぐ終わる処理 ✅ 無名関数(簡潔に)
再利用したい処理 ✅ 名前付き関数(明確に)
複雑 or デバッグしやすくしたい ✅ 名前付き関数(ログで見える)

✨lainさんの感性にぴったりの比喩:

無名関数は、舞台裏で支える“黒子”のような存在。 出しゃばらず、でも確実に動いている。 でも、照明が当たらないと「誰だったっけ?」ってなりやすい。



user icon
なるほど、ありがとうございます。w

ai icon
こちらこそ、“w” も含めて、そのままが素敵ですよ。
では続きの 4. ✅ Promiseと非同期処理の関係 に進みます!

4. Promiseと非同期処理の関係

いよいよ delay() の心臓部、「なぜ待てるのか?」 の仕組みに迫ります。

非同期処理とは?

JavaScriptは基本的に“1つのスレッド”で順番に処理します。
でも現実には:

  • 何秒か待ちたい
  • ネットからデータを取りたい
  • ファイルの読み込みを待ちたい

…など「すぐに終わらない処理」が必要になります。

これを “非同期処理(asynchronous)” と呼びます。

🤔 でも普通に待とうとすると?

function delay(ms) {
  const start = Date.now();
  while (Date.now() - start < ms) {
    // ずっと待ってる…(=画面も動かない)
  }
}

↑ このように「while文で強制的に止める」と、UIがフリーズして大問題になります。

そこで登場:Promise

Promise は「いつか終わる処理 」をラップして扱う仕組みです。

const promise = new Promise((resolve) => {
  setTimeout(() => {
    resolve("終わったよ");
  }, 2000);
});

このように書くと、promise は「2秒後に完了する」という約束(promise)になります。

await と組み合わせると…?

await promise;

と書くことで、「この行で一時停止して、完了するまで待つ」ことができます。 これがまさに await delay(2000) の仕組みです!

再掲:delay()の全体像

const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
  • new Promise(…) → 「2秒待つ処理」を約束として返す
  • resolve() → 「時間が来たら完了したよ!」と通知
  • await delay(…) → それを途中で止まって待つ

💡 ここまでの関係まとめ

構成要素 役割
Promise 「完了を約束する」ラッパー
resolve() 約束が果たされた瞬間を伝える
setTimeout() 時間の経過を使った非同期処理
await 完了するまで待つ

結論:

delay() は、Promise+setTimeout+await を使って
「プログラムを止めずに、指定時間だけ待つ」 という
JavaScriptらしい非同期処理の形になっています。

user icon
ありがとうございまいます。
昨日もお聞きしましたが、再度聞くとより鮮明にわかりますね。
次の「5. ✅ await delay(2000); の正体と実行例」お願いします。

ai icon

こちらこそ、本当にありがとうございます。
昨日の経験が“レイヤー”になって、今日の理解を深くしている──
まさに非同期的な学びのPromiseがresolveされた瞬間ですね。🌱✨

では、いよいよラスト:


5. await delay(2000); の正体と実行例

まずこの1行をおさらい

await delay(2000);

これが何をしているかというと…

「delay関数が返すPromiseが解決されるまで、“待つ”」

という意味です。

delay関数の中身(再掲)

const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
  • setTimeout(…, ms) で msミリ秒後にresolve()を実行
  • new Promise(…) が「完了を約束したオブジェクト」を返す
  • await がそれを “完了するまで止まる” という指示を出す

実行例:簡単なスリープ処理

async function testDelay() {
  console.log("処理開始");
  await delay(2000); // ← ここで2秒間ストップ
  console.log("2秒後に実行されました");
}
testDelay();

🕒 実行結果(出力タイミング):

処理開始
(2秒待つ)
2秒後に実行されました

ここで重要なのは:

  • delay() 自体は「すぐにPromiseを返している」
  • await がそのPromiseに「待て!」と命じている

という構造です。

もしawaitを使わなかったら?

function testDelay() {
  delay(2000);
  console.log("終わった");
}

この場合はすぐに “終わった” が表示され、待つことはできません。

つまり、非同期処理は書き方を間違えると“待たない”のがデフォルトなんですね。

実用例:演出やテンポに応用

async function dramaticText() {
  const messages = ["勇者は歩き出した…", "静かな夜に", "運命の鐘が鳴る"];
  for (let msg of messages) {
    console.log(msg);
    await delay(1500); // 1.5秒ごとに表示
  }
}

これはまさに、ゲームや演出の間(ま)を作る処理にぴったりです。

結論まとめ:

意味
delay(ms) ms後に解決されるPromiseを返す
await delay(ms) Promiseが完了するまで処理を止める
async function awaitを使える関数にする必要あり

これで await delay(2000); の中身が、「アロー関数+無名関数+Promise+setTimeout+await」 という “JavaScript非同期のオールスター構成” でできていることがすべてつながりました!🎉

ai icon

次は最後におまけとして:

応用例:アニメーションや演出への応用

を入れることもできますし、
記事としてこの章で区切っても十分に完結した備忘録になるかと思います。

続ける場合は教えてくださいね!📘✨



user icon

ありがとうございます、勉強になりました。

応用例:アニメーションや演出への応用

これもお願いします。


ai icon
ありがとうございます!
では最後に、6. 応用例:アニメーションや演出への応用 をお届けします。
ここまで理解した内容が、実際のゲームやWeb演出でどう活きるのか? を見てみましょう!

6. 応用例:アニメーションや演出への応用

delay() のようなスリープ処理は、一時停止して“間”を演出したい場面で非常に便利です。

ゲーム制作・UI演出・物語進行などで、大活躍します。

例1:メッセージを順に表示する

async function showDialogue() {
  const messages = [
    "……目を覚ました。",
    "ここはどこだろう……",
    "誰か……いますか?"
  ];
  for (let msg of messages) {
    console.log(msg);
    await delay(2000); // 2秒ごとに表示
  }
}

これは ノベルゲーム風のセリフ表示や、チュートリアル演出にも応用できます。

例2:カードを1枚ずつめくる

async function revealCards(cards) {
  for (let card of cards) {
    card.classList.add("flip");
    await delay(500); // 0.5秒おきに順にめくる
  }
}

→ カードゲームでの 演出テンポの調整に!

例3:ロゴ登場→メッセージ→ボタン

async function showTitleSequence() {
  document.getElementById("logo").style.opacity = "1";
  await delay(2000);

  document.getElementById("subtitle").style.opacity = "1";
  await delay(1500);

  document.getElementById("startButton").style.display = "block";
}

→ タイトル画面での **「余白」や「期待感」**の演出にぴったりです。

応用Tips:ループ×delayでランダム演出

async function sparkleLoop() {
  while (true) {
    createSparkle();
    await delay(Math.random() * 1000 + 300); // ランダム間隔
  }
}

これで パーティクルや光エフェクトが自然に発生する演出ができます。

結論

delay() は単なるスリープ関数ではなく、 「時間を操って、感情やテンポを演出できる道具」 なんです。

時間=演出、間=感情、テンポ=体験。

そう考えると、非同期処理の理解は“作品のリズムを生む力” だと言えるでしょう。

まとめ:この記事で得たもの

  • アロー関数、無名関数、Promiseの基礎
  • delay() の中身がどうできているか
  • 実際に“使える”演出例

まさに「理解 → 活用 → 表現」まで一気に繋がる、大きな学びのレイヤーになったと思います!