はじめに
この記事は以下の記事の続きになります。
ジェネレータ関数では、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()
メソッドはエラー発生時の制御に活用できます。