JavaScriptのジェネレータについて思うこと


JavaScriptにジェネレータってあるじゃないですか。それを使えば非同期の処理を同期みたいに書けるっていうのがあるじゃないですか。

function myroutine() {
	なんかする1;
	yield 1000;
	なんかする2;
	yield 2000;
	なんかする3;
	yield 1000;
	なんかする4;
}

function run_routine() {
	var g = myroutine();
	(function() {
		try {
			var msec = g.next();
		} catch (e if (e instanceof StopIteration)) {
			return;
		}
		setTimeout(arguments.callee, msec);
	})();
}

でもねーこれmyroutineを複数の関数に分割とかしたらそのままじゃ動かないじゃないですか

function myroutine() {
	myroutine_inner_A();
	yield 2000;
	myroutine_inner_B();
}

function myroutine_inner_A() {
	なんかする1;
	yield 1000;
	なんかする2;
}

function myroutine_inner_B() {
	なんかする3;
	yield 1000;
	なんかする4;
}

これをどうにかするためにはmyroutine_inner_A, myroutine_inner_Bを呼ぶときにforを使わないといけない

function myroutine() {
	for (var v in myroutine_inner_A()) yield v;
	yield 2000;
	for (var v in myroutine_inner_B()) yield v;
}

Luaのコルーチンだと普通に複数の関数に分割できるんだけどなー

function myroutine()
	myroutine_inner_A()
	coroutine.yield(2000)
	myroutine_inner_B()
end

function myroutine_inner_A()
	なんかする1
	coroutine.yield(1000)
	なんかする2
end

function myroutine_inner_B()
	なんかする3
	coroutine.yield(1000)
	なんかする4
end

でcoroutine.yieldってのはJSのyieldと違って単なる関数にすぎないのでコールバックとして渡すこともできる
だから以下のようなことも出来るはず! (LuaにはsetTimeoutなんてないので実際には動かないですけど)

function myroutine(wait_func)
	(wait_funcを指定ミリ秒待つ関数として使ってなにか実装)
end

function run_routine()
	local co = coroutine.create(function() myroutine(coroutine.yield) end)
	
	local function loop()
		local _, val = coroutine.resume(co)
		local status = coroutine.status(co)
		if status == "dead" then
			return
		end
		setTimeout(loop, val)
	end
	
	loop()
end

JavaScriptもこれぐらいできれば非同期処理を同期っぽく書けるって言われて納得できるんだけど、今のジェネレーターの機能だけじゃあ納得できない。
ずっともやもやしていたので記事にしてみました。

ツッコミ歓迎。