◆ ジェネレータ中の return は特別
◆ そこで終わるだけじゃなくて done を返す場合に返す値の使われ方も変わる
  ◆ [...gen()] の配列化などでは done のときの値は含まれない
  ◆ yield* でも done のときの値は含まれない

久々に generator を使ったら思ってたのと違う動きで戸惑いました

function* t() {
yield 1
yield 2
return 3
}

const x = t()
console.log(x.next())
console.log(x.next())
console.log(x.next())
console.log(x.next())

の結果は

{value: 1, done: false}
{value: 2, done: false}
{value: 3, done: true}
{value: undefined, done: true}

です
ここまでは想定どおりです

では

console.log([...t()])

これを実行した場合は?
done のところが 3 なので 1, 2, 3 の配列に思いました
しかし

[1, 2]

配列に含まれるのは yield で返した値のみで return で返した値 (done のとき) は配列に含まれないようです
return は通常の値ではなく終了理由とかそういう特殊な値を返す用途に使うことが想定されてるのでしょうか

とりあえず 条件を満たしたら最後の値を返しておしまいってときに return で値を返すのはダメみたいです

function* t() {
while (true) {
if (~~(Math.random() * 2)) {
return 100
} else {
yield 1
}
}
}
console.log([...t()])
// 100 は入らない

これでは最後に 100 は入らないので 100 も含めたいならこうします

function* t() {
while (true) {
if (~~(Math.random() * 2)) {
yield 100
return
} else {
yield 1
}
}
}
console.log([...t()])

単純に yield のあとに return を書くだけです

おまけ

ついでにもうひとつ yield* ではまりました
上のトラブルに出会ったのは単純なジェネレータではなく再帰的なジェネレータでちょっと複雑なものでした

function* t(values) {
function* sub(result, arr) {
const v = arr.shift()
if(v % 3 === 0){
return result
}else{
yield* sub([...result, v], arr)
console.log(".")
}
}
yield* sub([], values)
}
console.log(t([1, 2, 3, 4, 5, 6]).next())
// {value: undefined, done: true}

特に意味はない処理ですが 構造はこういう感じです
[...t()] で空になる理由がわからなくて とりあえず .next() の結果を一つ確認と console.log を置いてみたら console.log が 2 回実行されていました
その時の予想では yield* で任せてしまってるところで return してるから最初の 1 つの要素だけ帰ってくるというものです
return でおしまいなので console.log の部分は通らないと思ってました

そういうこともあってジェネレータがよくわからなくなってきていたのですが 上の通り return したのは [...] では含まれないとわかれば納得できる動きでした
[...] の配列化と同じように yield* でも return のときの値は無視されます
なので yield* sub(~) のところは返す結果なしで終わります
1 つ目の next の結果が見つからないので 結果が見つかるまで処理が続行されます
そして次の行が普通に実行されて console.log が呼び出されます
全再帰処理で同じように yield* で返す値がないので結局最後まで実行されておしまいとなっていました

このせいで ジェネレータって done になったらあとは結果返さないだけで最後までとりあえず実行されるの?なんて変な方向に考えていました
return 時の扱いが特別なのはジェネレータ使う上で思った以上に重要ですね