JavaScriptのyield
いわゆるジェネレーターというものについて
実行するにはJS1.7をあつかえるものが必要になります。
Firebugは対応してないので、xqjs - ’ellaneousを使うと便利です。
*注
"→"は"つまり"程度の意味で読んどいてください。
MDCのジェネレーターで扱われているフィボナッチ数列のyieldを例にする。
fib()なので、gに入るのはfibの実行結果。
通常ならreturnだが、yieldキーワードがfib内に存在する。
yield キーワードを含む関数(fib)はジェネレータという。
ジェネレーターを実行するとfib中身が実行されることなく、ジェネレータオブジェクト(ジェネレータ・イテレータ)という特殊なオブジェクトが返ってくる。(まだvar i=0なども起きてない)
→ まだ何も処理は行われてない
→ 返ってくるのはジェネレーターオブジェクト
*2
g.next()でyieldのところまで実行している。
yield i;はreturn i;と読み換えて(イメージ的)、返すのはi=0なので0、そしてそこで処理が止まる。
これで一回目のnext()は終わり。
次にg.next()すると、var t = i;から始まってwhileループしてるのでまたyieldのところで止まり、iの値を返す。
(g = g.next();としなくても、gは幾つ進んでるのを覚えている。)
つまり、yieldは渡された引数の値(iの事)を返して、同時にその行で処理を一時停止している。
- New in JavaScript 1.7 - MDC
- Kazuho@Cybozu Labs: JavaScript/1.7 で協調的マルチスレッド
- JavaScript1.7 の yield を使って、非同期処理を同期処理のように書く方法 - IT戦記
- Latest topics > JavaScript 1.7のyield文ってなんじゃらほ - outsider reflex
- JavaScript 1.7、yieldがわからない - はてなダイアリー - 無料で簡単。広告のないシンプルなブログをはじめよう!
- 元、失業SEの開発日誌: IEでもyieldが使えるよん
- yieldを使ってみた (JavaScript) SHOJI's Code - 各種言語のプログラミングコード
- gist: 394522 - GitHub XHR
- gist: 394548 - GitHub send
実行するにはJS1.7をあつかえるものが必要になります。
Firebugは対応してないので、xqjs - ’ellaneousを使うと便利です。
*注
"→"は"つまり"程度の意味で読んどいてください。
MDCのジェネレーターで扱われているフィボナッチ数列のyieldを例にする。
function fib() {
var i = 0, j = 1;
while (true) {
yield i;// 毎回ここで止まる
var t = i;
i = j;
j += t;
}
}
// *1
var g = fib();
for (var i = 0; i < 10; i++) {
// *2
p(g.next());
}
*1fib()なので、gに入るのはfibの実行結果。
通常ならreturnだが、yieldキーワードがfib内に存在する。
yield キーワードを含む関数(fib)はジェネレータという。
ジェネレーターを実行するとfib中身が実行されることなく、ジェネレータオブジェクト(ジェネレータ・イテレータ)という特殊なオブジェクトが返ってくる。(まだvar i=0なども起きてない)
→ まだ何も処理は行われてない
→ 返ってくるのはジェネレーターオブジェクト
*2
g.next()でyieldのところまで実行している。
yield i;はreturn i;と読み換えて(イメージ的)、返すのはi=0なので0、そしてそこで処理が止まる。
これで一回目のnext()は終わり。
次にg.next()すると、var t = i;から始まってwhileループしてるのでまたyieldのところで止まり、iの値を返す。
(g = g.next();としなくても、gは幾つ進んでるのを覚えている。)
つまり、yieldは渡された引数の値(iの事)を返して、同時にその行で処理を一時停止している。
■ジェネレータオブジェクトは以下のメソッドを持っている
- next()
- send(n)
- close()
追記:
yieldの返り値とsend()の意味合いは次のコードに詰まってる。
function f() {
p(1);// ... (a)
var value = yield 2;// ... (b)
p(value);// ... (c)
}
var g = f(); // この時点で f は実行されず、ジェネレーターオブジェクトを返す
p(g.next()); // ここで (a) まで実行される→yieldに渡した引数(2の事)をreturnして一時停止
g.send(3); // valueに3をセットして、next()する。
// (b)の次の行から、つまり(c)から実行する。(そしてStopIterationの例外が起きる)
実行結果は1,2,3と出てくる。■yieldを使った非同期処理を同期的に書く方法。
runnableはジェネレータを覆う関数。runnableの最後にnext();しておかないと、runnableの引数となる関数の仮引数(request)を実行した際には実体となってる関数function (t) { try{ o.send(t);catch(e) }}が実行されるので、sendはエラーになってしまう。
request(t)で実際にやることはo.send(t)になるので、引数に"hoge"としている場合、hogeを返す。
function runnable(f) {
var o;
o = f(function (t) { try{ o.send(t); }catch(e){} });
o.next();// ジェネレーターオブジェクトを作ったら一回目のnext();
}
runnable(function (request){
// yieldで一時停止
var result = yield setTimeout(function () {request("hoge");}, 2000);// ここの内部的な動きがイマイチ
p(result);// 2秒後にprintされる。
});
もう少し掘り下げてみる。上のコードは展開すると以下のような形になってる。
var f = function(request){
// yieldで一時停止
var result = yield setTimeout(function () {request("hoge");}, 2000);
p(result);// 2秒後にprintされる。
}
var o = f(function(t){ try{ o.send(t); }catch(e){} });// ジェネレーターが返る
o.next();// 停止してたのを動かす→setTimeoutが実行される
o.next();でyieldによって止まっていた関数が動き出すので、setTimeoutが実行されて、2秒後にrequest("hoge");つまりo.send("hoge");を実行することになる。
o.send("hoge")は resultに"hoge"をセットしてnext()となるので、resultには"hoge"が入り p(result);が実行される。
やっぱり
コメント(0件)
- TB-URL http://efcl.info/adiary/038/tb/