ジェネレーターの使い方(JavaScript)

  • #JavaScript

ジェネレーターの使い方 (JavaScript)

みなさん!こんにちは!

このたび、ライターとして、記事を執筆することになりました!

ネクストライブのかたやんです。

今回は、JavaScriptのジェネレーターの基本について書いていきます。

それでは、さっそく見ていきましょう!

ジェネレーターとは

ジェネレーターは、ECMAScript6で導入された、関数ブロック内でコードの実行を停止、再開する機能です。

ジェネレーターは、関数にアスタリスク(*)を付けることで定義できます(ジェネレーター関数)。

ジェネレーター関数は、ジェネレーターオブジェクトというオブジェクトを返します。

それでは、ジェネレーター関数の宣言を確認してみましょう。

//下記いずれもジェネレーター関数の宣言
function* generatorFn() {} 

let generatorVar = function* () {}

class generatorClass {
    * generatorFn() {}
}

上記のように、ジェネレーター関数を定義すると、ジェネレーター関数内でyieldというキーワードが使用可能になります。

次に、yieldというキーワードを確認してみましょう。

yieldとは

yieldはジェネレーター関数の処理を停止、再開させる機能をもっています。

yieldキーワードに処理が達したところで、処理が停止されnext()メソッドで処理を再開します。

yieldは、returnのような振る舞いをすると思っていただくと、親しみやすくなるかと思います。

では、実際に、どういう処理の流れになっているのか確認してみましょう。

next()

next()でyieldまで処理を進めます。yieldがなければ処理が終了となります。

⓵suspendedとは、処理が停止している状態を表しています。

next()を実行して、処理が終了していなければジェネレーターオブジェクトはdone: falseを返します。

処理が終了していれば、ジェネレーターオブジェクトはdone: trueを返すため、ジェネレーター関数の処理が終了したことが分かります。

next().valueでジェネレーターオブジェクトのvalueだけを抜き出すことができます。

⓸closedとなり、処理が終了したことを表しています。

//ジェネレーター関数の宣言
function* generatorFn() {
    let num = 1;
    yield num += 1;
    num += 1;
    yield num += 1;
    num += 1;
    yield num += 1;
    num += 1;
    yield num += 1;
    num += 1;
    return;
}

const generatorVar = generatorFn();

console.log(generatorVar); //generatorFn {<suspended>} ⓵
console.log(generatorVar.next()); //{value: 2, done: false} ⓶
console.log(generatorVar.next().value); //4 ⓷
console.log(generatorVar.next()); //{value: 6, done: false}
console.log(generatorVar.next().value); //8
console.log(generatorVar.next()); //{value: undefined, done: true}
console.log(generatorVar); //generatorFn {<closed>} ⓸

yieldで処理が停止し、next()で処理を再開し、次のyieldまで実行されることが確認できます。

以下のようにnext().value = 10とすることで、valueの値を変更することができますが、そのあとの処理はnext().value = 10の影響を受けていません。

console.log(generatorVar); //generatorFn {<suspended>}
console.log(generatorVar.next()); //{value: 2, done: false}
console.log(generatorVar.next().value = 10); //10
console.log(generatorVar.next()); //{value: 6, done: false}
console.log(generatorVar.next().value); //8
console.log(generatorVar.next()); //{value: undefined, done: true}
console.log(generatorVar); //generatorFn {<closed>}

return()

return()メソッドは、ジェネレーターをclosed状態にします。

⓵まだ、yieldが存在しているのにdone: trueになっていることが分かります。

//ジェネレーター関数の宣言
function* generatorFn() {
    let num = 1;
    yield num += 1; // num == 2
    yield num += 1; // num == 3
}

const generatorVar = generatorFn();

console.log(generatorVar); //generatorFn {<suspended>}
console.log(generatorVar.return()); //{value: undefined, done: true} ⓵
console.log(generatorVar); //generatorFn {<closed>}

returnの引数に値を入れると、valueにその引数が組み込まれます。

console.log(generatorVar.return(3)); //{value: 3, done: true}

throw()

throw()メソッドは、ジェネレーターオブジェクトにエラーを注入し、ジェネレーターをclosed状態にします。

//ジェネレーター関数の宣言
function* generatorFn() {
    let num = 1;
    yield num += 1; // num == 2
}

const generatorVar = generatorFn();

console.log(generatorVar); //generatorFn {<suspended>}
try {
    generatorVar.throw('errorが発生しました');
} catch (e) {
    console.log(e); //errorが発生しました
}
console.log(generatorVar); //generatorFn {<closed>}

yield*

yieldにアスタリスクを付けることで、イテラブルな処理が可能になります。

ジェネレーターを用いて配列の中身を一つ一つ取り出す場合、以下のような記述が考えられます。

function* generatorFn() {
  for (let i of [1, 2, 3]) {
    yield i;
  }
}

for (let g of generatorFn()) {
  console.log(g);
   //1
   //2
   //3
}

しかし、

⓵yield*キーワードを使用することで、以下のように簡単に記述することができます。

⓶yieldにアスタリスクを付けないと、イテラブルではなくなります。

//⓵
function* generatorFn() {
  yield* [1, 2, 3];
}

for (let i of generatorFn()) {
  console.log(i);
   // 1
   // 2
   // 3
}

//----------------------------------------------------------------
//⓶
function* generatorFn2() {
  //アスタリスクなし
  yield [1, 2, 3];
}


for (let i of generatorFn2()) {
  console.log(i);
//(3) [1, 2, 3]
}

まとめ

ジェネレーターは、

⓵ジェネレーター関数の処理を停止させるyieldキーワードに対応している。

⓶ジェネレーターオブジェクトを生成する。

⓷イテラブルな処理を少ないコードで実行できる。

⓸ジェネレーターはECMAScript6で導入された。

ということを紹介しました。

⓸からも分かるように、ジェネレーターは比較的新しい機能です。

そのため、Babelのような新しいJavaScriptのコードを古いバージョンに変換する機能(トランスパイル)等を使用する場合、不具合が発生する恐れがあるため注意が必要です。

ジェネレーターは、ちょっと変わったアプローチかもしれませんが、

この記事が、少しでも、あなたの役に立つことができれば幸いです。

この記事を書いた人

おすすめRECOMMENDED