Movatterモバイル変換


[0]ホーム

URL:


CtrlK
このページ内

ジェネレータ

function *は_ジェネレータ関数_の作成に使う構文です。ジェネレータ関数を呼び出すと、_ジェネレータオブジェクト_が返されます。ジェネレータオブジェクトは、イテレータインターフェースに準拠しています(つまりnextreturnおよびthrow関数を実装しています)。

ジェネレータが必要となる背景には2つの重要な動機があります。

遅延評価

ジェネレータ関数を使用して遅延評価されるイテレータを作成することができます。次の関数は、必要なだけの整数の無限リストを返します:

function*infiniteSequence() {var i=0;while(true) {yield i++;    }}var iterator=infiniteSequence();while (true) {console.log(iterator.next());// { value: xxxx, done: false } forever and ever}

もちろんイテレータが終了した場合は、以下に示すように{ done: true }の結果を得られます。

function*idMaker(){let index=0;while(index<3)yield index++;}let gen=idMaker();console.log(gen.next());// { value: 0, done: false }console.log(gen.next());// { value: 1, done: false }console.log(gen.next());// { value: 2, done: false }console.log(gen.next());// { done: true }

ジェネレータ関数の外部制御

これはジェネレータの真にエキサイティングな部分です。本質的には、関数がその実行を一時停止し、残りの関数実行の制御(運命)を呼び出し元に渡すことができます。

ジェネレータ関数は、呼び出した時には実行されません。単にジェネレータオブジェクトを作るだけです。サンプルの実行とともに次の例を考えてみましょう:

function* generator(){    console.log('Execution started');    yield 0;    console.log('Execution resumed');    yield 1;    console.log('Execution resumed');}var iterator = generator();console.log('Starting iteration'); // これはジェネレータ関数の本体の前に実行されますconsole.log(iterator.next()); // { value: 0, done: false }console.log(iterator.next()); // { value: 1, done: false }console.log(iterator.next()); // { value: undefined, done: true }

これを実行すると、次の出力が得られます。

$ node outside.jsStarting iterationExecution started{ value: 0, done: false }Execution resumed{ value: 1, done: false }Execution resumed{ value: undefined, done: true }
  • 関数はジェネレータオブジェクトに対してnextが呼び出された時に1回だけ実行されます

  • 関数はyield文が出現するとすぐに一時停止します

  • 関数はnextが呼び出されたときに再開します

つまり、基本的にジェネレータ関数の実行は、ジェネレータオブジェクトによって制御することができます。

ここまで、ジェネレータを使った通信は、ほとんどがジェネレータがイテレータの値を返す、一方向のものでした。JavaScriptのジェネレータの非常に強力な機能の1つは、双方向の通信を可能にすることです!

  • iterator.next(valueToInject)を使って、yield式の返却値を制御することができます

  • iterator.throw(error)を使ってyield式の位置で例外を投げることができます

次の例はiterator.next(valueToInject)を示しています:

function* generator() {    var bar = yield 'foo';    console.log(bar); // bar!}const iterator = generator();// 最初に`yield`された値を取得するまで実行するconst foo = iterator.next();console.log(foo.value); // foo// `bar`を注入して処理を再開するconst nextThing = iterator.next('bar');

次の例はiterator.throw(error)を示しています:

function* generator() {    try {        yield 'foo';    }    catch(err) {        console.log(err.message); // bar!    }}var iterator = generator();// 最初に`yield`された値を取得するまで実行するvar foo = iterator.next();console.log(foo.value); // foo// 処理を再開させ、`bar`エラーを発生させるvar nextThing = iterator.throw(new Error('bar'));

まとめると、このようになります:

  • yieldは、ジェネレータ関数の通信を一時停止し、関数の外部に制御を渡すことを可能にします

  • 外部から、ジェネレータ関数本体に値を送ることができます

  • 外部から、ジェネレータ関数本体に対して例外をthrowすることができます

これが、どのように便利なのでしょうか? 次のセクションasync/awaitでそれを説明します。

iteratorasync-await

最終更新

役に立ちましたか?


[8]ページ先頭

©2009-2025 Movatter.jp