はじめに
この記事は以下の記事の続きになります。
ジェネレータ関数では、next()の呼び出しごとにyield式までが実行されていました。
このyield式へは、next()メソッドの引数から値を渡すことが可能です。
本記事では、ジェネレータ関数で使用できるyieldへの値の受け渡しや、ジェネレータ関数の返り値、ジェネレータのreturn()メソッドやthrow()メソッドについてもみていきます。
next()の引数とyield
next()メソッドの引数に値を渡すことで、ジェネレータで前回値を返したyieldへ引数の値が渡されます。
前回呼び出されたnext()にはyieldから値が渡されていますが、今度はそのyieldへ次のnext()メソッドの呼び出し元から値を渡すことができるわけです。
next()に引数を渡す例をみてみます。
const generatorFn = function* (){
let y1 = yield 1
console.log('y1:',y1)
let y2 = yield 2
console.log('y2',y2)
let y3 = yield 3
console.log('y3:',y3)
}
const generator = generatorFn()
console.log(generator.next(2).value)
console.log(generator.next(3).value)
console.log(generator.next(5).value)
1
"y1:", 3
2
"y2", 5
3
この例では、3回next()を呼び出しています。
1回目の呼び出しではnext(2)で値を渡そうとしていますが、この呼び出しが初回ですので、引数の値2を渡す先のyieldがなく、無視されています。
2回目の呼び出しではnext(3)で前回のnext()呼び出しに対応するyield式へ値を渡しています。
その後、console.log('y1:',y1)が実行されてから、yield 2によって呼び出し元へ2が渡されています。
yield式は値を返した後、次のnext()呼び出しを待機します。
next()が呼び出されると、待機していたyield式へnext()の引数が渡され、次のyield式まで処理が進んでいきます。
この仕組みを利用することで、呼び出し側での値の状況に応じて、柔軟に対応できるわけですね。
ジェネレータ関数の返り値
ジェネレータ関数は返り値を返すこともできます。
この返り値は、反復結果オブジェクトのdoneプロパティがtrueになったときに、本来であればundefinedとなっているvalueプロパティからアクセスできます。
先の例のジェネレータ関数に返り値を設定してみます。
const generatorFn = function* (){
let y1 = yield 1
console.log('y1:',y1)
let y2 = yield 2
console.log('y2',y2)
let y3 = yield 3
console.log('y3:',y3)
return 42
}
const generator = generatorFn()
console.log(generator.next(2))
console.log(generator.next(3))
console.log(generator.next(5))
console.log(generator.next())
console.log(generator.next())
{ "value": 1, "done": false }
"y1:", 3
{ "value": 2, "done": false }
"y2", 5
{ "value": 3, "done": false }
"y3:", undefined
{ "value": 42, "done": true }
{ "value": undefined, "done": true }
ジェネレータ関数に返り値として42を設定しています。
この42はdoneがtrueとなったとき、つまりイテレータが終端に達したタイミングでのみ反復結果オブジェクトのvalue値に格納されています。
一度doneがtrueになった後には、再度next()を呼び出したとしても、ジェネレータ関数の返り値がvalueに格納されません。
ジェネレータのreturn()
ジェネレータ関数に返り値が設定できることをみてきましたが、ジェネレータからreturn()を呼び出すことも可能です。
ジェネレータからreturn()を呼び出すと、ジェネレータの処理を途中で終了させ、引数として渡した値をジェネレータの結果とすることができます。
先の例にreturn()メソッドの呼び出しを追加してみます。
const generatorFn = function* (){
let y1 = yield 1
console.log('y1:',y1)
let y2 = yield 2
console.log('y2',y2)
let y3 = yield 3
console.log('y3:',y3)
return 42
}
const generator = generatorFn()
console.log(generator.next(2))
console.log(generator.return(100))
console.log(generator.next(3))
console.log(generator.next(5))
console.log(generator.next())
console.log(generator.next())
{ "value": 1, "done": false }
{ "value": 100, "done": true }
{ "value": undefined, "done": true }
{ "value": undefined, "done": true }
{ "value": undefined, "done": true }
{ "value": undefined, "done": true }
この例では、2回目のnext()の呼び出しの前に、ジェネレータのreturn()メソッドを呼び出しています。
return()メソッドが呼び出されると、反復結果オブジェクトのdoneプロパティはtrueとなり、valueプロパティはreturn()に渡した引数の値100となっています。
以降のnext()の呼び出しでは、valueがundefinedで返されており、ジェネレータ関数の処理も行われていないことがわかります。
try/finallyと組み合わせることで、ジェネレータの終了時の処理を呼び出したり、クリーンアップ処理を行ったりと、return()メソッドによって処理の制御を行うことができます。
ジェネレータのthrow()
ジェエネレータにはreturn()メソッドの他にthrow()メソッドもあり、こちらは使用することで呼び出し側から例外を投げることが可能です。
先の例で追加したreturn()の代わりに、throw()を追加してみます。
const generatorFn = function* () {
try {
let y1 = yield 1
console.log('y1:', y1)
let y2 = yield 2
console.log('y2', y2)
let y3 = yield 3
console.log('y3:', y3)
return 42
} catch (e) {
console.log(e.message)
}
}
const generator = generatorFn()
console.log(generator.next(2))
console.log(generator.throw(new Error('error occurred')))
console.log(generator.next(3))
console.log(generator.next(5))
console.log(generator.next())
console.log(generator.next())
{ "value": 1, "done": false }
"error occurred"
{ "value": undefined, "done": true }
{ "value": undefined, "done": true }
{ "value": undefined, "done": true }
{ "value": undefined, "done": true }
{ "value": undefined, "done": true }
return()メソッドと同様に、throw()メソッドが呼び出されたあとにジェネレータの処理が終了していることがわかります。
catch内でもyieldで値を返すことも可能で、throw()メソッドはエラー発生時の制御に活用できます。