ジェネレータの解説と非同期への適用
update
2014-01-16
ご指摘頂いたので修正しました。ありがとうございます!
@Jxck_ 動画すごくわかりやすかった!一個、重箱の隅っこなんだけど、convert関数のapplyしてるところ、fn.apply(fn, args) になってるけど fn.apply(this, args) が正しい気がしました!
— Kazuhito Hokamura (@hokaccha) 2014, 1月 13
https://gist.github.com/Jxck/8380852 は修正済みです。動画の取り直しは勘弁して下さい(汗
- fn.apply(fn, args); + fn.apply(this, args);
intro
あけましておめでとうございます。 今年からはてなブログへ移行しました。
去年末くらいから流行っている Express の後継 Koa では JS の新機能ジェネレータが使われています。 このジェネレータは現在最新の node@11.10 などであれば --harmony-generator をつけることで、有効にすることができます。
今回はこのジェネレータの使いかたと、それを用いてどうやって非同期処理が書き換えられるかを、実演しながら解説したいと思います。
ちょっと長くなってしまったのですが、だいたい以下のような感じです。
- ~7:00:ジェネレータと yield と next() の使い方と、 next() と yield で値を受け渡す時の注意。
- 7:00~:ジェネレータで非同期をどうやって縦に書けるか、またエラーはどう処理するか、の解説。
なので、ジェネレータ自体がわかっている人は、 7:00 くらいから見ても大丈夫です。
これを見終わると、今流行?のあのライブラリの仕組みがなんとなくわかるかと。
Movie
how to use generator in node from Jxck on Vimeo.
完成品
https://gist.github.com/Jxck/8380852
まとめ
Generator の特徴は、処理を任意の時点で止めてそこから再開できることです。 これを上手く用いると、非同期処理の実行で一旦止め、コールバックが終了したら再開するということができます。
この時、 yield と next() で値をやりとりできるので、コールバック内でしか触れなかったデータ (err, data など) を、コールバックの外の世界に出すことができるようになりました。すると、これまでコールバックの世界でやっていたことを、わざわざコールバックの中でやらなくて良くなるため、コールバックの中でやるべきことは「コールバックの中で next(data) して、コールバックの外にデータを出す」という定形処理だけで良くなります。
Node の API は基本的にコールバックを引数の最後に取るので、これを上手く利用して、ユーザランドではコールバック以外の引数だけを設定し、定形のコールバックのライブラリ側で実行する。この時、コールバックに渡るデータは、 yield の評価結果としてジェネレータ内に戻し、エラーは throw() を用いて、ライブラリからジェネレータ内に戻します。(Generator 内では、 try-cache で取れます)
この、「Generator の世界とコールバックの世界の、データの橋渡し」と「定形コールバックの実行」を行っているのが co です。 そして、それによってユーザランドでは意識しないでよくなるコールバックを、 Node の API から消し去ってくれるのが thunkify です。
補足
ただし、この中で出てくる co, thunkify もどきは、本家のと比べればおもちゃ以下なので、 本家が中で何をやっているのかを知る手がかりとして見て頂ければと思います。 (本家を読むきっかけくらいにはなるかと。)
本当は co は thunkify 以外にも promise を上手く扱えるようになっているので、気が向いたら promise バージョンを撮ってみようかと思ってます。
harmony オプションが解禁されると、選択肢は非常に増えます。 去年は Koa と Angular な年感あったかもしれませんが、自分にとって今年はもう一段下の Object Observe とジェネレータの年になりそうだと思っています。