サーチ…


前書き

ジェネレータ関数( function*キーワードで定義)はコルーチンとして実行され、イテレータを介して要求される一連の値を生成します。

構文

  • 関数*名前(パラメータ){降伏値;戻り値}
  • ジェネレータ=名前(引数)
  • {値、完了} =ジェネレータ.next(値)
  • {value、done} = generator.return(value)
  • generator.throw(エラー)

備考

ジェネレータ機能は、ES 2015仕様の一部として導入された機能であり、一部のブラウザでは使用できません。また、 v6.0 、Node.jsで完全にサポートされています。詳細なブラウザ互換性リストについては、 MDNドキュメントを参照してください。ノードについては、 node.green Webサイトを参照してください。

ジェネレータ関数

ジェネレータ関数function*宣言で作成されます。呼び出されると、そのボディは即座に実行されません 。代わりに、 ジェネレータオブジェクトを返します。 ジェネレータオブジェクトは、関数の実行を「ステップスルー」するために使用できます。

関数本体内のyield式は、実行を中断して再開できるポイントを定義します。

function* nums() {
    console.log('starting');  // A
    yield 1;                  // B
    console.log('yielded 1'); // C
    yield 2;                  // D
    console.log('yielded 2'); // E
    yield 3;                  // F
    console.log('yielded 3'); // G
}
var generator = nums(); // Returns the iterator. No code in nums is executed

generator.next();  // Executes lines A,B returning { value: 1, done: false }
// console: "starting"
generator.next();  // Executes lines C,D returning { value: 2, done: false }
// console: "yielded 1"
generator.next();  // Executes lines E,F returning { value: 3, done: false }
// console: "yielded 2"
generator.next();  // Executes line G returning { value: undefined, done: true }
// console: "yielded 3"

早期反復出口

generator = nums();
generator.next(); // Executes lines A,B returning { value: 1, done: false }
generator.next(); // Executes lines C,D returning { value: 2, done: false }
generator.return(3); // no code is executed returns { value: 3, done: true }
// any further calls will return done = true 
generator.next(); // no code executed returns { value: undefined, done: true }

ジェネレータ関数にエラーを投げる

function* nums() {
    try {
        yield 1;                  // A
        yield 2;                  // B
        yield 3;                  // C
    } catch (e) {
        console.log(e.message);    // D
    }
}

var generator = nums();

generator.next();  // Executes line A returning { value: 1, done: false }
generator.next();  // Executes line B returning { value: 2, done: false }
generator.throw(new Error("Error!!"));  // Executes line D returning { value: undefined, done: true}
// console: "Error!!"
generator.next();  // no code executed. returns { value: undefined, done: true }

反復

ジェネレータは反復可能です。これはfor...of文でループされ、反復プロトコルに依存する他の構文で使用されます。

function* range(n) {
    for (let i = 0; i < n; ++i) {
        yield i;
    }
}

// looping
for (let n of range(10)) {
    // n takes on the values 0, 1, ... 9
}

// spread operator
let nums = [...range(3)];  // [0, 1, 2]
let max = Math.max(...range(100));  // 99

ここでは、ES6のジェネレータからカスタムの反復可能なオブジェクトを使用する別の例を示します。ここでは、匿名ジェネレータ関数function *使われています。

let user = {
  name: "sam", totalReplies: 17, isBlocked: false
};

user[Symbol.iterator] = function *(){

  let properties = Object.keys(this);
  let count = 0;
  let isDone = false;

  for(let p of properties){
    yield this[p];
  }
};

for(let p of user){
  console.log( p );
} 

ジェネレータへの値の送信

next()メソッドに渡すことで、ジェネレータに値を送ることができます

function* summer() {
    let sum = 0, value;
    while (true) {
        // receive sent value
        value = yield;
        if (value === null) break;

        // aggregate values
        sum += value;
    }
    return sum;
}
let generator = summer();

// proceed until the first "yield" expression, ignoring the "value" argument
generator.next();

// from this point on, the generator aggregates values until we send "null"
generator.next(1);
generator.next(10);
generator.next(100);

// close the generator and collect the result
let sum = generator.next(null).value;  // 111

他のジェネレータに代理

ジェネレータ関数内から、コントロールはyield*を使って別のジェネレータ関数に委譲することができます。

function* g1() {
  yield 2;
  yield 3;
  yield 4;
}

function* g2() {
  yield 1;
  yield* g1();
  yield 5;
}

var it = g2();

console.log(it.next()); // 1
console.log(it.next()); // 2
console.log(it.next()); // 3
console.log(it.next()); // 4
console.log(it.next()); // 5
console.log(it.next()); // undefined

イテレータ - オブザーバインタフェース

ジェネレータは、 IteratorObserverという2つの要素の組み合わせです。

イテレータ

反復子は、呼び出されたときにiterableを返すものです。 iterableはあなたが反復できるものです。 ES6 / ES2015以降、すべてのコレクション(Array、Map、Set、WeakMap、WeakSet)はIterableコントラクトに準拠しています。

ジェネレータ(イテレータ)はプロデューサです。反復において、消費者は生産者からの価値をPULL

例:

function *gen() { yield 5; yield 6; }
let a = gen();

あなたが呼び出すたびa.next()あなたは基本的にしているpullイテレータから値を-ingとpauseで実行yield 。次回a.next()を呼び出すと、実行は以前に一時停止した状態から再開します。

観察者

ジェネレータはまた、いくつかの値をジェネレータに戻すことができるオブザーバです。

function *gen() {
  document.write('<br>observer:', yield 1);
}
var a = gen();
var i = a.next();
while(!i.done) {
  document.write('<br>iterator:', i.value);
  i = a.next(100);
}

ここでは、 yield 1がある値に評価される式のように使われることがわかります。それが評価される値は、 a.next関数呼び出しの引数として送られた値です。

したがって、 i.valueが最初にi.valueされる値( 1 )になり、次の状態への反復を続けると、 a.next(100)を使用して値をジェネレータにa.next(100)ます。

ジェネレータとの非同期処理

ジェネレータは、関数がジェネレータを取り込み、非同期コードを同期的に書き込むことを可能にする、(taskJSまたはcoからの) spawn関数で広く使用されていspawn 。これは、非同期コードが同期コードに変換される/同期して実行されることを意味しません。 syncように見えるコードを書くことはできますが、内部的にはまだasyncです。

同期はブロック中です。非同期は待ちます。ブロックするコードを書くのは簡単です。プーリングすると、割り当て位置に値が表示されます。 PUSHingの場合、コールバックの引数位置に値が表示されます。

イテレーターを使用するときは、プロデューサーから値をPULLします。あなたがコールバックを使用すると、プロデューサーのPUSHコールバックの引数の位置に値をES。

var i = a.next() // PULL
dosomething(..., v => {...}) // PUSH

ここでは、の値を引くa.next()と第二に、 v => {...}コールバックであり、値はPUSH引数位置にED vコールバック関数の。

このプッシュプッシュメカニズムを使用して、このような非同期プログラミングを書くことができます。

let delay = t => new Promise(r => setTimeout(r, t));
spawn(function*() {
  // wait for 100 ms and send 1
  let x = yield delay(100).then(() => 1);
  console.log(x); // 1

   // wait for 100 ms and send 2
  let y = yield delay(100).then(() => 2);
  console.log(y); // 2
});

だから、上記のコードを見ると、 blockingれているような非同期コードを書いています(yieldステートメントは100ms待ってから実行を続けます)。しかし、実際にはwaitingます。ジェネレータのpauseresumeプロパティは、この素晴らしいトリックを行うことができます。

どのように機能するのですか?

スポーン関数は、 yield promiseを使用して、ジェネレータからプロミスステートをPULLし、プロミスが解決されるまで待機し、解決された値をジェネレータに戻して消費するようにします。

今すぐ使用する

したがって、ジェネレータとスポーン機能を使用すると、NodeJSのすべての非同期コードを同期しているように見えるように整理できます。これにより、デバッグが容易になります。また、コードはきれいに見えます。

この機能は、 async...awaitようにJavaScriptの将来のバージョンに向けられasync...awaitます。しかし、ライブラリで定義されているspawn関数(taskjs、co、またはbluebird)を使用して、ES2015 / ES6で今日使用することができます

ジェネレータによる非同期フロー

ジェネレータは、一時停止してから実行を再開できる機能です。これにより、qやcoを中心とした外部ライブラリを使って非同期関数をエミュレートできます。基本的には、非同期の結果を待つ関数を書くことができます:

function someAsyncResult() {
    return Promise.resolve('newValue')
}

q.spawn(function * () {
    var result = yield someAsyncResult()
    console.log(result) // 'newValue'
})

これにより、同期しているかのように非同期コードを書き込むことができます。さらに、いくつかの非同期ブロックで作業を試してみてください。約束が拒否された場合、エラーは次のキャッチによってキャッチされます。

function asyncError() {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            reject(new Error('Something went wrong'))
        }, 100)
    })
}

q.spawn(function * () {
    try {
        var result = yield asyncError()
    } catch (e) {
        console.error(e) // Something went wrong
    }
})

coを使うと、 q.spawn代わりにco(function * (){...})と全く同じようにq.spawn



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow