18
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

javascriptのyieldとジェネレータ関数について

Last updated at Posted at 2021-10-09

概要

yieldおよびジェネレータ関数について勉強したことをまとめてみます。

yieldとは

  • Javascriptにおけるジェネレータ関数で処理を一時停止したり再開したりするのに使われるもの
  • 一般的な関数のreturnに相当するもので、ジェネレータ関数ではyieldを使用する
  • returnと違って関数内で何回でも呼び出すことができる
  • 参考:yield

ジェネレータ関数(Generator)とは

  • イテレータを生成する関数
  • イテレータとは配列のような繰り返し処理できるもののこと
  • 書き方
/* ジェネレータ */
function* testGenerator() {
    for(let i=1; i<=10; i++){
        if (i % 2 == 0)
            yield i;
    }
}

// イテレータを生成
g = testGenerator()

// 繰り返し処理
for (let even of g) {
    console.log(even);
}

// next()を使用する場合
// 上記繰り返し処理ですべての要素を呼び出したので、もう一度作り直す
g = testGenerator()

// next()の返り値は  { value: 2, done: false } のようになっている
console.log(g.next()); // { value: 2, done: false }
console.log(g.next()); // { value: 4, done: false }
console.log(g.next()); // { value: 6, done: false }
console.log(g.next()); // { value: 8, done: false }
console.log(g.next()); // { value: 10, done: false }
console.log(g.next()); // { value: undefined, done: true } valueがundefinedになってdoneは初めてtrueになる

あらためてyieldとは

  • ジェネレータ関数.next()では
    1. 一回目の呼び出しは最初のyieldで一旦処理をストップ。
    2. 二回目は最初のyieldから二回目のyieldまでを処理しストップ。
    3. 三回目は...(以下繰り返し)となる。
function* testGenerator() {
    console.log('yield 1!');
    yield 1;
    console.log('yield 3!');    
    yield 3;
    console.log('yield 5!');    
    yield 5;
}

g = testGenerator()

console.log(g.next().value);
console.log(g.next().value);
console.log(g.next().value);

// または
// for (let odd of g) {
//     console.log(odd);
// }

// 結果
// yield 1!
// 1
// yield 3!
// 3
// yield 5!
// 5

yield*

  • ジェネレータ関数内で別のジェネレータ関数を呼び出す際に使用する
  • next()を使用した際の返り値は「呼び出したジェネレータ関数の順」になる
function* testGenerator() {
    yield 1;
    yield 3;
    yield 5;
}

function* testGenerator2() {
    yield 2;
    yield 4;
    yield 6;
}

function* parentGenerator() {
    yield* testGenerator();
    yield* testGenerator2();
}

g = parentGenerator()

for (let num of g) {
    console.log(num);
}

// 結果
// 1
// 3
// 5
// 2
// 4
// 6

ジェネレータ関数に引数を渡したい

  • ジェネレータ関数には引数を渡せます。
  • 渡した引数は関数内のどこからでも参照できます。
function* testGenerator(val) {
    console.log(val);
    yield 1;
    console.log(val + 1);
}

let g = testGenerator(123);

g.next();
g.next();

// 結果
// 123
// 124
  • さらにジェネレータ関数.next(引数)とすることで渡すこともできます
  • 渡した引数はyield時に取り出すことができます。
function* testGenerator() {
    let result = yield 1;
    yield result + 2;
}

let g = testGenerator();

console.log(g.next(g.next().value).value);

// 結果
// 3

yieldを使った同期処理・非同期処理

  • yieldの「処理を一時停止したり、再開したりする」機能を用いて非同期処理を同期的に処理することができます。
  • 例えばこちらの関数
function async(){
    let count = 1;
    setTimeout(() => {
        console.log(count++);
        setTimeout(() => {
            console.log(count++);
            setTimeout(() => {
                console.log(count++);
            },1000)
        },1000)
    },1000)
}

async();

// 1秒毎に1, 2, 3と表示される

yieldを使えば以下のように書き換えられます

function async(g, num){
    setTimeout(() => {
        console.log(num);
        g.next();
    }, 1000)
}

g = (function* () {
    yield async(g, 1);
    yield async(g, 2);
    yield async(g, 3);
})();

g.next();

元々ネストを浅くする目的で使われていたようですが、現在はasync, awaitが主流のようです。

function test(num){
    return new Promise(resolve => {
        setTimeout(() => {
            console.log(num);
            resolve();
        }, 1000)
    });
}

async function g(){
    await test(1);
    await test(2);
    await test(3);
}

g();

疑問

ジェネレータ関数内でreturnするとどうなるか

function* testGenerator() {
    yield 1
    yield 2;
    return 'return';
    console.log('hello');
    yield 3;
}

g = testGenerator();
console.log(g.next());
console.log(g.next());
console.log(g.next());
console.log(g.next());

// 結果
// { value: 1, done: false }
// { value: 2, done: false }
// { value: 'return', done: true }
// { value: undefined, done: true }
  • returnを呼び出した時点でdone: trueとなる
  • return以降の処理は例えyieldの記載があっても呼ばれることはない。
18
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?