MENU

ジェネレーター関数とは|JavaScript基礎

目次

ジェネレーター関数とは


JavaScriptのジェネレーター関数とは、function* キーワードを使って宣言される特殊な関数です。通常の関数は function キーワードで宣言され、呼び出すと最初から最後まで一気に実行されます。しかし、ジェネレーター関数は function* のように末尾にアスタリスク(*)を付けて宣言し、処理の途中で「一時停止」できる点が大きな特徴です。これにより、関数の実行を分割して制御することが可能になります。

イティセル/コード専門官

通常の関数は functionキーワードで宣言します。  

ジェネレーター関数はfunction の直後にアスタリスク(*)を付けて、function* と書きます。

ジェネレーターでは、yield を使って値を返しつつ処理を停止し、呼び出し側が next() メソッドを使うことで再開できます。その際に返されるオブジェクトには、返却された値を示す value プロパティと、関数が終了したかどうかを示す done プロパティが含まれています。

つまりジェネレーター関数は、「一時停止と再開を自在に行える関数」であり、イテレーション処理や状態管理に強みを持つ仕組みなのです。

イティセル/コード専門官

yield が存在する間は「はい!次はあるね!」と返しながら処理を一時停止し、呼び出し側が next() を実行すると再開されます。
そして、すべての yield を通過して最後まで到達すると「はい!次はもうないので終わり!」と返され、ジェネレーターの処理は終了します。

ジェネレーター関数

イティセル/コード専門官

ジェネレーター関数を理解する第一歩として、コード全体を見てみましょう。

function* sampleGenerator() {
  yield 1;
  yield 2;
  yield 3;
}

const gen = sampleGenerator();
console.log(gen.next());
console.log(gen.next());
console.log(gen.next());
console.log(gen.next());
リクナ / JavaScript統括官

{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: undefined, done: true }

実行結果
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: undefined, done: true }

1. ジェネレーター関数の定義

イティセル/コード専門官

ジェネレーターでは yield によって値を一つずつ取り出しながら処理を進めることができます。つまり、sampleGenerator の中では 1 → 2 → 3 と順番に進んでいくイメージです。

function* sampleGenerator() {
  yield 1;
  yield 2;
  yield 3;
}

2. ジェネレーターの呼び出し

イティセル/コード専門官

const gen = sampleGenerator(); を実行した時点では、ジェネレーター関数の本体はまだ動いていません。ここで返される gen は「ジェネレーターオブジェクト」であり、next() メソッドを持っています。この next() を呼び出すことで初めて関数の処理が進み、yield によって値が一つずつ返されていきます。

つまり、gen は繰り返しを行うための準備が整った状態であり、sampleGenerator()を呼び出すことで「next()が実行されるまでは待機する仕組み」がセットされるのです。

const gen = sampleGenerator();

3. `next()` の動作

イティセル/コード専門官

console.log(gen.next()); を実行すると、next()が呼び出されてジェネレーター関数の処理が開始されます。
関数は最初の yield まで進み、yield 1; に到達した時点で値 1 を返し、そこで処理が一時停止します。

console.log(gen.next());

4. 戻り値の構造

イティセル/コード専門官

gen.next() の戻り値はオブジェクトであり、そこには2つのプロパティが含まれています。
ひとつは value で、これは yield によって返された値を表します。今回の例では 1 が返されます。
もうひとつは done で、関数が終了したかどうかを示す真偽値です。false の場合はまだ残りの処理があり、true の場合はすべての処理が終了したことを意味します。

したがって、最初の呼び出しでは { value: 1, done: false } が返され、「値として 1 を返したが、関数の処理はまだ続いている」という状態になります。

実行結果
{ value: 1, done: false }

5. 続けて呼び出すと…

イティセル/コード専門官

2回目の next() 呼び出しでは、ジェネレーターが yield 2 に到達し、値として 2 を返します。続く3回目の呼び出しでは yield 3 に到達し、値 3 を返します。
そして4回目の呼び出しでは、 yield 4 が存在しないため、戻り値は { value: undefined, done: true } となり、ジェネレーターの処理は終了します。

gen.next();
gen.next();
gen.next();
リクナ / JavaScript統括官

{ value: 2, done: false }
{ value: 3, done: false }
{ value: undefined, done: true }

実行結果
{ value: 2, done: false }
{ value: 3, done: false }
{ value: undefined, done: true }

基本構文と仕組み

•  yield:値を返しつつ関数を一時停止。

•  next():停止した関数を再開し、次のyieldまで進める。

•  戻り値:{ value: any, done: boolean } のオブジェクト。

イティセル/コード専門官

yield は値を返しつつ関数の処理を一時停止する役割を持っています。
そして next() を呼び出すことで、停止していた関数が再開され、次の yield まで処理が進みます。
その際の戻り値は { value: any, done: boolean } という形式のオブジェクトであり、value には返された値が、done には関数が終了したかどうかを示す真偽値が格納されます。

ジェネレーターのユースケース

イテレーション制御

•  メモリ効率が良い  

イティセル/コード専門官

例えば数百万件のログや巨大ファイルを一度に読み込むとメモリを圧迫するが、ジェネレーターなら1件ずつ処理できます。

•  処理の分割が可能  

イティセル/コード専門官

yieldによって「ここまで処理したら一旦止める」という制御ができるため、長時間の処理を小さく分けて扱えます。

•  ストリーミング処理に適している  

イティセル/コード専門官

ネットワークから届くデータやセンサーからの入力など、逐次的に流れてくるデータをリアルタイムで処理できます。
要するに、流れてくるデータを一度に全部まとめて処理するのではなく、届いた順に少しずつ処理できることです。

非同期処理の簡略化

1.  コールバック関数

イティセル/コード専門官

非同期処理では、その結果を受け取るためにコールバック関数を渡す方法がよく使われていました。
しかし、この方法では処理が複雑になるにつれて関数の入れ子が深くなり、コードの可読性が大きく低下してしまいます。この状況は「コールバック地獄」と呼ばれ、非同期処理を扱う上で大きな課題となっていました。

2.  Promise

イティセル/コード専門官

Promiseを使うことで、非同期処理を「成功(resolve)」と「失敗(reject)」で表現できるようになり、処理をチェーンとしてつなげて書けるようになりました。
これにより、従来のコールバック関数よりも見通しの良いコードを書くことが可能になったのです。ところが、複雑なフローを扱う場合には .then() .then() .catch() といった記述が長く続き、かえって直感的に理解しづらくなるという課題も残っていました。

3.  ジェネレーター + Promise

イティセル/コード専門官

function*yield を使うことで、「処理を一時停止し、Promiseの結果を待ってから再開する」という流れを表現できるようになりました。これによって、非同期処理でありながらも、あたかも同期処理のように見えるコードを書くことが可能になったのです。

状態管理

状態(state):アプリケーションが持つ「今の情報」や「現在の状況」。  

イティセル/コード専門官

例えば、ログインしているとか、カートに入っている商品一覧を見てる、画面に表示しているなどがあります。要するに、今やってるっていうイメージです。

状態管理:この状態を一貫して扱い、変更が起きたときに正しく画面や処理へ反映させる仕組み。

イティセル/コード専門官

状態管理は、ユーザーがどの画面にいても同じ情報を正しく扱えるようにするために重要であり、アプリの一貫性や使いやすさを支える仕組みです。
例えば「カートの中身を見てるか」「ログイン状態なのか」「再生中か停止中か」などです。

非同期処理におけるジェネレーターの役割

非同期処理の歴史を振り返ると、まずES6でジェネレーターが導入されました。ジェネレーターは function* と yield を使うことで処理を一時停止し、Promiseの結果を待ってから再開するという流れを表現でき、当時は「同期処理のように見える非同期コード」を書くための手段として注目されました。その後、ES2017で async/await が登場し、非同期処理をより直感的かつ簡潔に記述できるようになったため、現在では非同期処理の主流となっています。

ただし、ジェネレーターが完全に役割を失ったわけではありません。async/await が得意なのは「非同期処理の直線的な流れ」を書くことですが、ジェネレーターは「カスタムイテレーション」や「逐次処理」に強みを持っています。例えば、大量データを少しずつ処理したり、状態遷移を一歩ずつ制御したりする場面では、ジェネレーターの「一時停止と再開」という仕組みが依然として有効です。また、Redux-Saga のようなライブラリでは、ジェネレーターを活用して非同期処理と状態管理を組み合わせる実装が行われています。

イティセル/コード専門官

例えば動画配信サービスのように、大量のデータを少しずつ処理してユーザーに届ける仕組みでは、ジェネレーターの「一時停止と再開」という性質が役立ちます。

応用例

無限シーケンス生成(カウンターや乱数)

イティセル/コード専門官

無限シーケンス生成とは、カウンターや乱数のように終わりのない連番を作り続ける仕組みのことです。これを利用すると、ページネーションやIDの発行など、必要に応じて次々と新しい番号を生成できるため、アプリケーションの中で継続的にユニークな値を扱う場面に役立ちます。

イティセル/コード専門官

ids.next().value を呼び出すたびに、ジェネレーターが次の値を返してくれる。呼び出し回数が増えれば、その分だけ返される値も増えていきます。

function* idGenerator() {
  let id = 1;
  while (true) {
    yield id++;
  }
}
const ids = idGenerator();
console.log(ids.next().value);
console.log(ids.next().value);
console.log(ids.next().value);
console.log(ids.next().value);
console.log(ids.next().value);
console.log(ids.next().value);
リクナ / JavaScript統括官

1
2
3
4
5
6

実行結果
1
2
3
4
5
6

遅延評価(Lazy Evaluation)

イティセル/コード専門官

遅延評価(Lazy Evaluation)とは、大量のデータを一度に処理するのではなく、必要になった分だけを取り出して処理する仕組みのことです。この方法を用いることで、メモリの使用量を抑えながら効率的にデータを扱うことができ、大規模なデータ処理に適しています。

イティセル/コード専門官

例えば次のコードでは、1から5までの数値を順番に取り出すように設定しています。もし範囲を1から8までに変更すれば、実行結果は 1,2,3,4,5,6,7,8 となります。

function* range(start, end) {
  for (let i = start; i <= end; i++) {
    yield i;
  }
}
for (const num of range(1, 5)) {
  console.log(num);
}
リクナ / JavaScript統括官

1,2,3,4,5

実行結果
1,2,3,4,5

カスタムイテレーターの実装

イティセル/コード専門官

カスタムイテレーターの実装とは、オブジェクトに独自の反復処理を付与する仕組みのことです。
通常の配列や文字列は標準で反復処理(for...of など)に対応していますが、カスタムイテレーターを定義することで、任意のオブジェクトにも「順番に値を取り出す」動きを持たせることができます。
これにより、オブジェクトの内容や構造に合わせた柔軟な反復処理を実現でき、データの扱い方を直感的に表現することが可能になります。

イティセル/コード専門官

この例では、customIterable の中に A〜C という値があり、独自の反復処理によって順番に取り出せるようになっています。
for...of で呼び出すと処理が実行され、結果は A, B, C となります。なお、呼び出すまでは処理は動かず、必要になったときに初めて値が生成される仕組みです。

const customIterable = {
  *[Symbol.iterator]() {
    yield "A";
    yield "B";
    yield "C";
  }
};
for (const val of customIterable) {
  console.log(val);
}
リクナ / JavaScript統括官

A, B, C

実行結果
A, B, C

状態マシンの実装

イティセル/コード専門官

状態マシンの実装とは、アプリケーションの状態遷移を逐次的に表現する仕組みのことです。ある状態から次の状態へと順番に移り変わる流れを明確に定義できるため、複雑な処理や分岐を整理して直感的に扱うことができます。これにより、状態管理を一貫して行えるだけでなく、予測可能な動作を保証できるのが特徴です。

具体的な用途としては、Reduxのような状態管理ライブラリにおける状態遷移ロジックの表現や、ゲームにおけるキャラクターの行動パターン(例:待機 → 攻撃 → 回避 → 再び待機)などがあります。状態マシンを導入することで、複雑な挙動をシンプルな「状態の切り替え」として表現でき、開発や保守が容易になります。

イティセル/コード専門官

下記のコードを見ると、通常のイテレーター関数では要素をすべて返すと「次はありません」と表示されますが、今回のジェネレーターは while(true) によって無限に繰り返せます。
つまり、console.log(light.next().value); を呼び出す回数が増えるほど、結果も繰り返し返ってくる仕組みになっています。

function* trafficLight() {
  while (true) {
    yield "Green";
    yield "Yellow";
    yield "Red";
  }
}
const light = trafficLight();
console.log(light.next().value);
console.log(light.next().value);
console.log(light.next().value);
console.log(light.next().value);
console.log(light.next().value);
リクナ / JavaScript統括官

Green
Yellow
Red
Green
Yellow

実行結果
Green
Yellow
Red
Green
Yellow

コルーチン的利用(双方向通信)

イティセル/コード専門官

コルーチン的利用(双方向通信)とは、ジェネレーターの yield を使って関数と呼び出し元が「お互いに値をやり取りできる」仕組みのことです。

普通の関数は「呼び出し元 → 関数 → 戻り値」という一方向の流れしかありません。  
でもジェネレーターを使うと、

•  呼び出し元から値を渡す
•  関数がその値を受け取って処理する
•  さらに新しい値を返す

という キャッチボールのようなやり取り ができます。
これによって、関数と呼び出し元が対話的に動作し、処理を少しずつ進めたり、状態を持ちながら柔軟に制御できるのが特徴です。

イティセル/コード専門官

下記のコードを見てみると、まるで会話のような動きになっています。yield は「質問を投げる」ような役割を持ち、呼び出し元から値を渡すことで「回答」を受け取ることができます。

const d = dialog(); でジェネレーターを宣言すると、まだ処理は始まっていません。  
最初に d.next().value を呼ぶと、ジェネレーターが動き出して “What is your name?” を返し、そこで一旦止まります。  
次に d.next("iteliseru").value を呼ぶと、呼び出し元から “iteliseru” という値がジェネレーターに渡され、内部の name に代入されます。その後 “Hello, iteliseru!” を返します。

function* dialog() {
  const name = yield "What is your name?";
  yield `Hello, ${name}!`;
}
const d = dialog();
console.log(d.next().value);
console.log(d.next("iteliseru").value);
リクナ / JavaScript統括官

“What is your name?”
“Hello, iteliseru!”

実行結果
"What is your name?"
"Hello, iteliseru!"

まとめ

ジェネレーター関数は function* と yield を使って処理を一時停止・再開できる特殊な関数です。この仕組みによって、通常の関数では難しい柔軟な制御が可能になります。具体的には次のような強みがあります。

•  逐次処理  

大量のデータを一度に読み込むのではなく、yield を使って1件ずつ取り出せます。例えば数百万件のログを扱う場合でも、必要な分だけ順番に処理できるため、メモリを効率的に使えます。

•  非同期処理  

yield と Promise を組み合わせることで「処理を一時停止し、結果が返ってきたら再開する」という流れを表現できます。これにより、非同期処理をあたかも同期処理のように書けるため、複雑なコールバック構造を避けられます。

•  状態管理  

アプリケーションの状態遷移を一歩ずつ制御できます。例えば「ログイン → 商品選択 → 決済」といった流れを yield で区切ることで、状態の切り替えを明確に表現できます。ゲームのキャラクター行動(待機 → 攻撃 → 回避)なども同様に管理可能です。

•  双方向通信  

yield で値を返すだけでなく、next(value) で呼び出し元から値を受け取ることができます。これにより「質問を投げる → 回答を受け取る → 応答を返す」というキャッチボールのようなやり取りが可能になり、関数と呼び出し元が対話的に動作します。

もしこの記事が役に立ったと思ったら、シェアやコメントで教えてください。  いただいた声を今後の改善に活かしていきます。  最後まで読んでくださり、本当にありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

ITTIのアバター ITTI 運営長

私はフロントエンドエンジニアを目指す初心者で、ITパスポートを取得済みです。現在はCopilotを活用しながらAIや最新のIT技術を学び、日本の開発現場で求められるチーム開発やセキュリティの知識を吸収しています。学んだことはコードや仕組みを整理し、わかりやすく発信することで、同じ学びの途中にいる人たちの力になりたいと考えています。

コメント

コメントする

目次