nsIInputStreamPumpの非同期処理にジェネレータを使う
こいつの使いかたをググると、onDataAvailableのたびに配列(メッセージキュー)に並べておいて、上から順番に処理するコードがよく見つかる。
実はAjaxのとき話題になったんだが、ジェネレータで、こういった非同期処理を同期処理のように書ける。
この実験のために、いわゆるechoサーバをlocalhostの60000番ポートに立ててある。
"yield" == "onDataAvailableまでsleep".
let p = Application.console.log; function nettest() { //ソケット(トランスポート)を作る let TransportService = Cc["@mozilla.org/network/socket-transport-service;1"]. getService(Ci.nsISocketTransportService); let transport = TransportService.createTransport(null, 0, "localhost", 60000, null); let line = {}; //非同期処理にジェネレータを渡す。 callWithClientSocket(transport, function(cistream, costream) { //サーバに"hello\n"を送って、 costream.writeString("hello\n"); //待つ。 yield; //サーバから返事が来たら1行読む。 cistream.readLine(line); //コンソールにプリント。 p(line.value); //サーバに"world\n"を送って、 costream.writeString("world\n"); //待つ。 yield; //サーバから返事が来たら1行読む。 cistream.readLine(line); p(line.value); //サーバに"こんにちは\nせかい"を送って(UTF-8でおk) costream.writeString("こんにちは\n"); costream.writeString("せかい"); //待つ. yield; cistream.readLine(line); p(line.value); cistream.readLine(line); p(line.value); yield; }); } function socketInputStream(aTransport) { //ソケットの入力ストリームをとる let istream = aTransport.openInputStream(0, 0, 0); //ソケットの入力ストリームの文字コードをUTF-8にする let cistream = Cc["@mozilla.org/intl/converter-input-stream;1"]. createInstance(Ci.nsIConverterInputStream); cistream.init(istream, "UTF-8", 0, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); //ソケットの入力ストリームを行単位で読むようにする cistream.QueryInterface(Ci.nsIUnicharLineInputStream); //ソケットの入力ストリームの非同期ポンプをとる let pump = Cc["@mozilla.org/network/input-stream-pump;1"]. createInstance(Ci.nsIInputStreamPump); pump.init(istream, -1, -1, 0, 0, false); return [pump, cistream]; } function socketOutputStream(aTransport) { //ソケットの出力ストリームをとる let ostream = aTransport.openOutputStream(0, 0, 0); //ソケットの入力ストリームの文字コードをUTF-8にする let costream = Cc["@mozilla.org/intl/converter-output-stream;1"]. createInstance(Ci.nsIConverterOutputStream); costream.init(ostream, "UTF-8", 0, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); return costream; } function callWithClientSocket(aTransport, aProc) { //ソケットの入出力ストリームと、非同期ポンプをとる。 let [pump, cistream] = socketInputStream(aTransport); let costream = socketOutputStream(aTransport); // Javascript 1.7 の新機能、ジェネレータを作る。 let generator = aProc(cistream, costream); //非同期ポンプのリスナ let listener = { generator: generator, //onDataAvailableでの"環境"はちょっと特殊。 transport: aTransport, //onDataAvailableでの"環境"はちょっと特殊。 onStartRequest: function onStartRequest(channel, context) { }, onStopRequest: function onStopRequest(channel, context, status, errorMsg) { this.generator.close(); this.transport.close(null); }, onDataAvailable: function onDataAvailable(channel, socketContext, inputStream, sourceOffset, count) { //非同期ポンプにデータが来るたびに、ジェネレータを進める。 this.generator.next(); } }; generator.next(); //こいつをonStartRequestに書くと動かないのは、なんで? pump.asyncRead(listener, null); }
小ネタ
- Firefox3拡張を書くなら、もうvarは捨ててletでいいとのこと(id:nanto_viさんに教えてもらった。thx.)。C言語構文の皮をかぶったSchemeの世界征服計画が進んでいるぜ。ふっふっふ。
- echoサーバはGaucheユーザリファレンス: gauche.selector - 簡単なディスパッチャからパクった。
- 最近は、XPCOMのIOやソケットの、Gauche風のラッパーを書きためている。callWithClientSocketという関数名もGaucheからパクった。
- Ajaxにジェネレータを使うのは有名だけど、"nsIInputStreamPump yield"でググっても出てこない。既出だったら教えてください。