テック技報最新号へ →
コード

JS: ジェネレーターは .return() / .throw() で外から止めたり例外を入れたりできる

ジェネレーターのオブジェクトは next() のほかに、外から終了させる return(value) と、止まっている yield の位置に例外を投げ込む throw(error) を持っている。どちらも中の finally が動くので、後片付けをジェネレーターの中に書ける。

@__syumai · 2026-11-11
#javascript#generator#iterator

使う場面

  • ファイルや接続を開いて 1 件ずつ yield するジェネレーターで、呼び出し側が途中で抜けても確実に閉じたい
  • 呼び出し側で起きたエラー(タイムアウトなど)をジェネレーターの中に知らせて、中で後始末や別の処理をさせたい

やり方

js
function* rows() {
  console.log("open");
  try {
    yield 1;
    yield 2;
    yield 3;
  } catch (e) {
    console.log("中で受け取った:", e.message);
    yield -1;               // 例外を受けて続行もできる
  } finally {
    console.log("close");   // return() でも throw() でも必ず通る
  }
}

// 1) return(): 外から終わらせる
const a = rows();
a.next();            // open → { value: 1, done: false }
a.return("stop");    // close → { value: "stop", done: true }

// 2) throw(): 止まっている yield の位置で例外が起きる
const b = rows();
b.next();                        // { value: 1, done: false }
b.throw(new Error("timeout"));   // 中で受け取った: timeout → { value: -1, done: false }
b.next();                        // close → { value: undefined, done: true }

for...of を break や return・例外で抜けると、言語が自動で return() を呼ぶ。だから後片付けは finally に書いておけば、呼び出し側は何も意識しなくてよい。

js
for (const r of rows()) {
  if (r === 2) break;   // ここで return() が呼ばれ、"close" が出る
}

注意点

  • finally の中に yield があると、return() しても終わらずにその値を done: false で返して止まる。終了を止められてしまうので、finally で yield は書かない。
  • 中で catch しなかった例外は throw() の呼び出し元に投げ返され、ジェネレーターは終了する。
  • まだ一度も next() していないジェネレーターに throw() すると、中の try に入る前なので受け取れず、そのまま外に投げ返される。
  • 非同期ジェネレーター(async function*)でも同じで、return() / throw() は Promise を返す。for await...of を抜けたときも return() が呼ばれる。

出典

  • @__syumai(ECMAScript 仕様輪読会 #esspec で Generator を読んだ感想)
X の元の投稿を開く