Kawashima本書について本書は、Node.jsでのアプリケーション開発を始めようとする皆さんに、”高度な”JavaScriptについて知るべきあらゆることを解説します。World”チュートリアルの、はるか上をいくものです。ステータス貴方が読んでいるのは、本書のいわゆる最終版となります。本書は、間違いが見つかった場合や、Node.jsの新バージョンにおえる変更点を反映する時のみ、改訂されます。最終更新日は2012年2月12日です。本書内のコードのサンプルは、Node.jsのバージョン0.6.10でテストしています。著者と同じようなバックグラウンドをもつ読者にとって最もフィットする内容となっています。本書はデータ型や変数、制御構造などの本当に基本的な事柄はカバーしないということになります。本書を理解するためには、そのような基本的な事柄は読み進める時点で理解している必要があります。JavaScriptの関数やオブジェクトは他の言語のものと異なるので、より詳細に解説していきます。本書を読み終わる頃には、貴方はひとつのWebアプリケーションを完成させることになります。そのWebアプリケーションでは、ユーザがWebページを閲覧し、ファイルをアップロードすることができます。世界を変えるようなものができるわけではないですが、これらの機能を実現するために”必要最低限”のコードを書くだけではありません。一歩先へ進み、アプリケーションの持ついくつかの相違点をきれいに分離した、シンプルだけれども完全なフレームワークを作りあげることができます。私が言わんとしていることは、すぐにわかることでしょう。Node.jsを使ったJavaScriptでの開発は、ブラウザ内のJavaScriptの開発と何が違うのかを見てみるところから始めます。World”アプリケーションを作ります。これは確かに”何かをする”、最も基本的なNode.jsのアプリケーションです。私たちが構築したいと考えている”現実の”アプリケーションの種類について議論します。このアプリケーションを組み立てるために実装が必要となるパーツを分解し、それぞれのパーツをステップバイステップで作っていきます。約束通り、これらを通してJavaScriptのより先進的なコンセプトを知り、どう活用し、そしてなぜ他のプログラミング言語とは異なるコンセプトが意味をなすのかを学ぶことになります。Githubリポジトリからダウンロードできます。技術的な話に入る前に、貴方とJavaScriptの関係について話をしておきましょう。本章は、この文書をさらに読み進めることが貴方にとって意味があるのか、判断してもらいたいのです。貴方が私と同じであれば、はるか昔HTMLの文書を書いてHTMLでの”開発”を始めたはずです。JavaScriptと呼ばれるなんだか面白そうなものに出会い、つまり貴方のWebページに相互作用を加えるような、とても基本的な使い方だけで利用していたことでしょう。貴方はもっと”本物”が欲しくなり、複雑なWebサイトを構築する方法を学びたくてPHP、Ruby、Javaなどのプログラミング言語を勉強し、”バックエンド”となるコードを書き始めたのではないでしょうか。それにも関わらず、JavaScriptに注目してみるとjQueryやPrototypeなどが目に入ってきました。JavaScriptの世界では物事が進化し、もはやこの言語は、window.open()では済まないのだ、ということがわかってきたのです。それは序章に過ぎません。結局のところ貴方はまだ、JavaScriptのユーザでしかなく、デベロッパーではなかったのです。いま、Node.jsにたどり着きました。サーバ側でのJavaScriptって、凄いの?そろそろ、古くて新しいJavaScriptをチェックする時期がきたことを確信しました。待って、Node.jsアプリケーションを書くというのはとても厄介。やって書く必要があるのか、理解しないといけない、つまりJavaScriptを理解しなければいけないのです。ここで問題があります。JavaScriptには2つ、もしかすると3つの時代があるため(90年代半ばの小さくて可愛らしいDHTMLの助っ人、jQueryなどのもう少し真剣なフロントエンド、そして現在のサーバサイド)、JavaScriptを利用するのではなくてJavaScriptで開発しているのだと感じさせてくれるような、”正しい”使い方を示す情報を探すのは容易ではありません。貴方は経験豊富なデベロッパーなのですから、なんとなく時間を無駄に過ごしたり間違った使い方をしたりして新しいテクニックを身につけたい、なんていうことはないでしょう。正しい角度でアプローチしていきたいはずです。素晴らしい文書はそこら中にあります。一つの文書では十分でないこともあります。必要なのはガイダンスなのです。私の目的は、貴方にガイドを提供することなのです。世の中には本当に素晴らしいJavaScriptエンジニアがいます。本当に、私は前段で述べたような男に過ぎません。バックエンドのWebアプリケーションを1つや2つ知っているだけで、まだまだ”本当の”JavaScriptは学び始めたばかりですし、Node.jsについても同じです。JavaScriptのより先進的な側面を学んだのは最近で、経験も豊富ではありません。本書は”初心者から上級者へ”なるためのではなくて、どちらかというと”初心者から初級者へ”というものです。私がNode.jsを学び始めた時にあったらよかった、そんな文書になっているはずです。サーバサイドJavaScript最初にJavaScriptが日の目を見たのは、ブラウザ上でした。これは単なるコンテキストに過ぎません。コンテキストによってその言語でできることは決まってきますが、それはその言語自体ができることとイコールというわけではありません。JavaScriptは”完全な”言語であり、様々なコンテキストで使えます。他の言語でやっていることは、すべてJavaScriptでもできます。Node.jsもまた、ひとつのコンテキストに過ぎません。Node.jsによって、JavaScriptはバックエンド、ブラウザの外で動作できるのです。バックエンドでJavaScriptが動作するには、インタープリターで変換され、そして実行されなければなりません。これをNode.jsが行います。VMが利用されています。Chromeが使用しているJavaScriptの実行環境そのものと同じです。それに加えて、Node.jsにはたくさんの便利なモジュールが同梱されています。全てを1から作る必要はないのです。コンソールに文字列を出力するモジュールなどがあります。Node.jsは、実行環境とライブラリの2つから成っているのです。これを使うためには、Node.jsをインストールする必要があります。公式サイトのインストール手順を訪れて、またここに戻ってきて下さい。World”を書いてみるとしましょう。好きなエディタを選んだら、helloworld.jsというファイル名で保存して下さい。World”を標準出力に書き出します。Worldと出力されたはずです。次はもっと本物っぽいものを書いてみましょう。画像がhttp://domain/uploadにアップロードされ、アップロードが一旦完了したらそこに表示される結構です。Googleで検索して、何か組み合わせていけばできてしまうでしょう。ここでやりたいのはそういうことではないのです。目的を達成するためだけの最も基本的なコードだけを書く、それをやりたいだけではないのです。綺麗で正確なコードを書きたいのです。ここからは、もっと複雑なNode.jsアプリケーションを構築していく時の感覚を得るために、わざと必要以上に抽象的にしていきます。アプリケーションスタックそれではアプリケーションを分解していきましょう。やりたいことを満たすためには、一体どんなものを実装したら良いのでしょうか。Webページを提供したい。我々のサーバでは、リクエストが要求しているURLによってそれぞれ別に応答する必要がある。よってリクエストをリクエストハンドラに結びつけるためのルータのようなものが必要サーバに届き、ルータによって渡されたリクエストの内容に応えるために、リクエストハンドラが必要ルータはどんなPOSTデータでも取り扱えるべきで、そのデータをリクエストハンドラに使い易い形式で渡す必要がある。よってリクエストデータハンドリングが必要URLへのリクエストだけを扱いたいのではなく、それらのURLが要求された時に内容を表示したい。細かい部分の面倒を見るアップロードハンドリングが必要ちょっと考えてみて下さい。PHPでこのスタックを作り上げるとしたら、どうやって実現するでしょうか。HTTPサーバとmod_php5をインストールするのでしょう。考えると、”Webページを提供して、HTTPリクエストに応答する必要がある”という部分については、PHPの中で起きているわけではないことに気づきます。nodeの場合、状況が少し異なります。Node.jsでは、アプリケーションのみを実装するのではなく、HTTPサーバ全体を実装することになります。我々がこれから作ろうとしているWebアプリケーションとWebサーバは基本的に同一のものなのです。すると大変そうな印象を持たれてしまったかもしれませんが、Node.jsでは、そうでもないことがすぐにわかるはずです。それではスタックの最初の部分である、HTTPサーバから始めてみましょう。基本的なHTTPサーバはじめて私が”本当の”Node.js”アプリケーションを作り始めようとした時は、どうやってコードを書くのかわかりませんでしたし、コードをどう構成したら良いかもわかりませんでした。1つのファイルに全てを書くので良いのでしょうか。Webで見つかるチュートリアルではほとんどの場合で、Node.jsの基本的なHTTPサーバが、全てのロジックを1カ所で持っているようなものでした。次の機能を実装しようとした時、コードの可読性を維持することは果たしてできるのでしょうか。比較的簡単だということがわかりました。することで、Node.jsで実行するメイン部分のファイルを綺麗な状態に保つことができます。メインファイルから使ったりモジュール同士で相互利用したりするモジュールも、すっきりさせることができます。アプリケーションを起動するためのメインファイルと、HTTPサーバのコードから利用されるモジュールファイルを作りましょう。私の印象では、メインファイルにはindex.jsという名前にするのが標準的です。サーバモジュールにはserver.jsというファイル名を付けます。サーバモジュールから始めます。このプロジェクトのルートディレクトリにserver.jsという名前のファイルを作成します。response.end();}).listen(8888);これだけです!これで実際に動くHTTPサーバが書けました。実行して、ちゃんと動くかテストしてみましょう。server.jsそれでは、ブラウザを開いてhttp://localhost:8888/にアクセスしてみましょう。World”と表示しているWebページが表示されるはずです。これは、とても気になるのではないでしょうか。我々のプロジェクトをどう構成していくのか、というテーマを少し離れて、この部分について深堀してみませんか。本題に戻ることを約束します。HTTPサーバの分析ではでは、何が起こっていたのか、分析していきます。最初の行で、httpモジュールをrequire(要求)しています。変数httpを通して可能になります。次に、httpモジュールが提供する関数のひとつであるcreateServerを呼出しています。この関数はオブジェクトを返しますが、このオブジェクトがlistenというメソッドを持っています。listenメソッドはポート番号を数値の引数として受け取り、HTTPサーバがそのポート番号で待ち受けします。今のところはhttp.createServerの括弧内にある関数定義の箇所は無視しておいて下さい。http.createServer();server.listen(8888);これでHTTPサーバが8888番ポートで待ち受けをして、その他、何もすることはありません。(たとえリクエストがきても応答さえしません)(もしPHPのような、より保守的な言語の経験者であれば特に)面白いのは、createServer()の呼出しの第1引数に関数定義があることではないでしょうか。(そして最後の)引数なのです。JavaScriptでは、関数をその他の値と同様、あちこちに渡すことができるのです。"Hello");よく読んでみて下さい!ここでは、関数sayをexecute関数の第1引数として渡しています。sayの戻り値ではなく、say自体を渡しているのです!ここではsayが、関数executeの中にあるローカル変数someFunctionになっています。executeの中では(この変数に括弧をつけて)someFunction()と書くことができるのです。sayは引数を1つとるので、executeからsomeFunctionを呼び出す時には、さらに引数を渡すことができます。何も関数の定義を先にしたうえで、それを渡す、といった手順を踏まなくても良いのです。"Hello");まさにexecuteが第1引数を期待している場所で、executeに渡したい関数を定義しています。ここで見たやり方をすると、関数に名前をつけてやる必要がありません。function)と呼びます。これは”上級の”JavaScriptと呼んでいるものの最たる例ですが、ステップバイステップでやっていきましょう。今のところ、JavaScriptでは関数を呼び出す時の引数として関数を渡すことができる、ということを受け入れて下さい。定義した関数を変数として割り当ててから渡すこともできますし、その場で関数を定義して渡すこともできるのです。response.end();}).listen(8888);もうここで何をしているのかは明確なはずです:createServer関数に匿名関数として渡しているのです。Node.jsがどのように動作するかの原理に従っているものだから、ということになります。それはつまり、イベント駆動型のことであり、高速に動作する理由でもあります。node.js”は一読の価値があります。つまるところ、Node.jsはイベント駆動で動作するということです。えー、はい、私もです。何を言っているのかさっぱりわかんない。頑張って説明してみようと思います。WebベースのアプリケーションをNode.jsで書くことに意味があるのかを。当然ながらサーバの待ち受けは同じポートでしないようにしたいですし、サーバにHTTPリクエストが届いた時は、他にも何かしたいわけです。いかなる時でも起こりますし、サーバ内では動かせるのは単一のプロセスしかないのです。PHPのアプリケーションを書く時は、このようなことに困る事はありません:HTTPリクエストがあった時はいつでも、(だいたいはApacheなどの)Webサーバがこのリクエストのために新しいプロセスをフォークし、PHPスクリプトが最初から最後まで実行されることになります。制御フローについていうとポート8888番に新しいリクエストが届いた時には、Node.jsプログラムの中に入ってきており、おかしなことにならないようそれをうまく取り扱わないといけません。これに精通するためにはいくつか新しい概念を学ぶ必要がありますが、まさにここが、Node.js/JavaScriptのイベント駆動設計が役に立つ部分なのです。新しい概念をどのようにサーバのコードに適用するのかを見ていきましょう。サーバは先ほど作りました。サーバを作る時には関数をメソッドに渡しました。リクエストを受け付けた時は必ず、渡した関数が呼ばれます。実際起こるかどうかはわかりませんが、入ってくるリクエストを取り扱うための場所を作りました。その渡した関数のことです。最初に定義したとか、匿名で渡したとかは置いておきましょう。この概念をコールバックと呼びます。メソッドに関数を渡した時、メソッドに紐づいたイベントが発生していた場合に、この関数をコールバックするのです。少なくとも私にとっては、これを理解するのは大変でした。Felixのブログエントリを読んでみて下さい。それではこの新しい概念を使って遊んでみましょう。我々のコードが実行を続けることを証明できるでしょうか。started.");onRequest関数(コールバック関数)が呼ばれた時、テキストをconsole.logで出力していることに注目して下さい。その他のテキストはHTTPサーバ開始直後に出力しています。started.”とコマンドライン上で出力されると思います。received.”というメッセージがコマンドライン上に出力されます。received.”を2回出力したのではないでしょうか。開いた時、ブラウザがfaviconをロードしようとしているためです。)サーバによるリクエストの取り扱い方OK、サーバのコードの残り部分をサクッと解析してしまいましょう。残りはコールバック関数onRequest()の本体です。コールバックによってonRequest()が呼ばれた時、2つの引数、requestとresponseが渡されます。これらはオブジェクトです。発生したHTTPリクエストや、そのリクエストへの応答の詳細を取り扱うことができます(例えば、サーバにリクエストを行ったブラウザに対して実際に何かを送り返す、というようなこと)。World”をHTTPレスポンスのボディーとして送ります。さいごにresponse.end()を呼び出してレスポンスを完了しています。この時点ではrequestオブジェクトは全く使っていないので、リクエストの詳細についてはまだ触れません。サーバモジュールの場所の確保OK、それでは約束通り、アプリケーションの構成の話に戻りましょう。先ほどはserver.jsというファイルにとても基本的なHTTPサーバのコードを書きました。アプリケーションのブートストラップや他のアプリケーションモジュールを使用してアプリケーションを起動するindex.jsというメインファイルを置くことが一般的だという話をしました。(server.jsにある他のHTTPサーバモジュールと同じです)。本当のNode.jsモジュールにする方法について話していきましょう。require("http");...http.createServer(...);Node.jsのどこかに”http”と呼ばれるモジュールがあるので、コード内でrequireを行い、その結果をローカル変数に割り当てることでそれを使うことができます。これによりローカル変数にはオブジェクトが割り当てられ、httpモジュールが提供する全てのパブリックメソッドを使うことができます。require("http");...foo.createServer(...);Node.js内部のモジュールの使い方はわかりました。自分でモジュールを作り、それを使うためにはどのようにしたら良いのでしょうか。server.jsスクリプトをモジュールとして作り直してみましょう。そんなに変更する必要はありません。コードをモジュール化するためには、それを必要とするモジュールのスクリプトに提供するように、パーツが持っている機能をexportする必要があります。今のところ、exportしたいHTTPサーバの機能は単純なものです:我々のサーバモジュールは単にサーバを起動するだけのスクリプトです。start;このようにすれば、サーバのコードがserver.jsファイルの中にある状態で、メインファイルindex.jsを作り、そこでHTTPサーバを起動することができます。require("./server");server.start();すぐおわかりだと思いますが、サーバモジュールを他の内部モジュールと同じように使うことができます:そのファイルをrequireし、変数に割り当てれば、exportされた関数を使うことができるのです。我々のアプリをメインスクリプトから起動してみましょう。index.js素晴らしいですね。再度つなぎ合わせることができました。アプリケーションの最初の部分、HTTPリクエストを受け付ける、という部分しか残っていません。–ブラウザからリクエストのあったURLによって応答のしかたを変えたいですね。単純なアプリケーションとしては、onRequest()コールバック関数の中で直接やることもできます。先ほど述べた通り、もう少しだけ抽象レイヤーを追加して、サンプルアプリケーションを面白くしてみましょう。–では、routerと名付けてモジュールを作っていきましょう。我々のアプリケーションの第3の部分となるものです:つまり、リクエストを受信した時に実際に働くリクエストハンドラ群です)。HTTPリクエストの中を見て、リクエストURLやGET、POSTパラメータを抽出しなければいけません。ルータの一部としてやるべきか、サーバの中でやるべきか(もしくはモジュール自身か)という点は議論の余地があるかもしれませんが、とりあえず現時点ではHTTPサーバの一部分としてやってみましょう。必要な情報はすべて、コールバック関数onRequest()の第一引数として渡されるrequestオブジェクトから得ることができます。内容を解釈するためには、Node.jsの追加のモジュール、つまりurlとquerystringが必要となります。urlモジュールが提供するのは、URLの各部分(例えば、リクエストパスやクエリ文字列)を抽出するメソッドです。querystring(string)["hello"]もちろん、querystringを使ってPOSTリクエストのパラメータをパースすることもできるのですが、それは後で見てみることにします。–これでリクエストを、(これから書くことになる)ルータを使ってURLパスに基づくリクエストハンドラにマッピングすることができます。アプリケーションのコンテキストにおいて、/startというURLに対するリクエストと、/uploadというURLに対するリクエストは、それぞれ別のコードに渡すことができるわけです。OK、実際のルータのコードを書いてみましょう。今のところそれでOKです。色々なロジックをルータの中に書いていく前に、どうやってこのルータをサーバと連携させるのかを見ていきましょう。我々のHTTPサーバは、ルータのことを知り、利用する必要があります。start;そしてindex.jsも拡張しましょう。require("./router");server.start(router.route);また出てきました。関数を渡していますね。これはもう目新しいことではないはずです。今一度、少しの間だけ関数型プログラミングについての話をしても良いでしょうか。関数を渡すのは、技術的な思慮によるものだけではありません。哲学的とさえいえます。考えてみて下さい:indexファイルは、routerオブジェクトをサーバに渡します。サーバはこのオブジェクトのroute関数を呼びます。このように、モノを渡して、サーバが何かをスルためにこれを使うのです。ねえルータさん、これをルーティングしてくれない?しかしサーバは何かする必要はありません。最終的に何か必要なものがあるわけではなく、とにかくアクションを必要とします。必要なのは名詞ではなく、動詞なのです。私は関数型プログラミングをとても良く理解することができました。Nounsを読んだ時にこの考え方を理解することができたのです。今すぐアクセスしてこれを読んで下さい。私が今まで読んだソフトウェアに関する書き物の中でも、出会えて嬉しいと思える、最高のものです。本当のリクエストハンドラへのルーティングさて、仕事に戻るとしましょう。HTTPサーバとリクエストルータは、我々の意図した通り、お互いに会話ができる仲良しの友達となりました。それだけでは不十分です。”ルーティング”とは、個々のリクエストを別々に取り扱いたい、ということを意味しています。私たちが欲しいのは、/uploadへのリクエストや/startへのリクエストをそれぞれ取り扱う”ビジネスロジック”なのです。今、ルーティングはルータの内部で”終了”しています。これはアプリケーションがもっと複雑になった時にスケールできなくなるからで、ルータはリクエストに対して実際に何かを”する”場所ではないわけです。リクエストが引き渡されるリクエストハンドラを呼び出しましょう。リクエストハンドラがないと、今までルータを使ってやろうとしていた事の意味がなくなってしまいます。-驚くような事はここでは一切ありません。upload;こうして、ルータにルーティングする先のものを渡すことで、リクエストハンドラとルータを繋ぎ合わせることができます。この時点で私たちは決断をする必要があります:リクエストハンドラのモジュールはルータの中にハードコーディングするのか、もしくは少しだけ依存性の注入を行うのか。依存性の注入は、他のパターンも同様ですが、使いたいというだけで使うべきではありません。このケースでは、ルータとリクエストハンドラを粗に結合することでルータの再利用性を高められるという意味がしっかりあります。リクエストハンドラはサーバからルータに渡す必要があることを意味していますが、これはまだ間違っているような感じがします。説明するとしたら、メインファイルからサーバに渡し、そこからさらにルータに渡す、ということになります。やってそれを渡すのでしょうか。今現在、2つのハンドラがあります。実際のアプリケーションでは、この数はもっと増えたり減ったりします。新しいURLやリクエストが追加される度に、リクエストをハンドラにマッピングするというつまらない作業をしたくはありません。yといったようなことをするのでは美しくありません。要素の数が変わって、文字列(おそらくリクエストURL)にマッピングする?ということは、なんだか連想配列がうまくはまりそうですね。悪いニュースがあります。JavaScriptでは、連想配列が提供されていないのです。連想配列が必要なら、オブジェクトを使えば良いのです!これについては、次のURLに良い記事があります。http://msdn.microsoft.com/en-us/magazine/cc163419.aspx関連する箇所を引用します:C++やC#において、オブジェクトについて話をする時は、クラスや構造体のインスタンスについて触れます。(つまり、クラスのことです)に依存します。JavaScriptのオブジェクトは、これに当てはまりません。JavaScriptでは、オブジェクトは単なる名前と値のペアの集合なのです。文字列をキーにしたディクショナリであると考えて下さい。関数でも良いのです!OK、ではコードに戻ってみましょう。粗結合にするためにこのオブジェクトをroute()に入れてしまいます。動詞を使って命名したいと思います。次に出てくるルータのところで、より自然な表現をすることができるからです。異なるURLを同じリクエストハンドラにマッピングする箇所がとてもシンプルになっています:“/”とrequestHandlers.startというキーと値のペアを追加することで、/startへのリクエストだけでなく/へのアクセスも同じstartハンドラに渡す処理が、とても美しくあざやかに実現できていますね。オブジェクトが定義できたら、それを追加の引数としてサーバに渡します。start;引数handleをstart()関数に追加して、ハンドルオブジェクトをroute()コールバックの第一引数として渡すコードに修正しました。route()関数も同様に変更しましょう。route;ここで何をしているかというと、パス名に対応するリクエストハンドラが存在するかどうかをチェックし、もし存在する場合は、紐付けられた関数を単に呼び出しています。先述のように実に自然なhandle[パス名]();として、あたかも”このパス名をhandleして下さい”と言っているように表現することができます。あとは実際にリクエストハンドラが何かブラウザに送り返せば良いわけですね。ひとつ思い出してみて下さい。World”は、server.jsファイルのonRequest()関数が出しているものです。“ハンドリングリクエスト”とは、”リクエストに応答する”ということを意味していて、このonRequest関数がしているように、リクエストハンドラにブラウザと会話をしてもらう必要があります。私たちがやりたいと思うような単刀直入なアプローチは間違えやすい可能性があります:魔法のように動いて、もっともらしくて、そして突然予期せず失敗に終わってしまうのです。“単刀直入なアプローチ”というのはつまりこういうことです:ユーザに対して表示したい内容をリクエストハンドラにreturn()させ、このレスポンスデータをonRequest関数でユーザに戻す。良いアイデアではないことがわかるはずです。リクエストハンドラから始めましょう。ブラウザに表示させたい内容を戻すようにします。upload;これでよし。同じように、ルータはリクエストハンドラによって返されたものをサーバに渡してやる必要があります。route;これを見ておわかりのとおり、リクエストがルーティングされなかった時にもいくらかのテキストを返すようにしています。最後に、ルータ経由でリクエストハンドラが渡してきた内容をブラウザに返すよう、サーバを改修します。start;書き直したアプリケーションを起動すれば、すべては魔法のようにうまく行くことでしょう。found”となります。後述のノンブロッキング操作を行いたいとなった場合に問題となってしまうのです。それでは詳しい説明に入りたいと思います。問題が発生してしまいます。ブロッキングでの操作について、そしてノンブロッキングの操作について説明していきます。“ブロッキング”と”ノンブロッキング”の方法を説明するかわりに、まずはいまのリクエストハンドラにブロッキングの操作を追加したら何が起こるのかを見てみましょう。Start”文字列を返すまで10秒間待つようにします。sleep()の類いはJavaScriptにないので、ちょっとしたうまい方法で工夫してやる必要があります。Start”と返します。待つことなくすぐに返します。実際にはブロッキング操作を行う部分で、ある種もっと長い計算処理が行われるということを想像して下さい)この変更で何が起こるのか見てみましょう。いつも通り、サーバを再起動する必要があります。2つのブラウザウィンドウもしくはタブを開き、1つ目のブラウザウィンドウのアドレスバーにhttp://localhost:8888/startと入力して下さい。このURLを開かないで下さい!次に2つめのブラウザウィンドウのアドレスバーに、http://localhost:8888/uploadと入力し、1つ目と同様、まだenterを打たないで下さい。切り替えてenterを打ちます。このような現象に気づくでしょう:/startのURLの方はロードされるのに10秒間かかります。これは期待通りの動きです。/uploadのURLの方もロードに10秒間かかってしまっています。こっちのリクエストハンドラではsleep()していないのに!なぜでしょう?それは、start()がブロッキング操作をしているからなのです。”他のものが動くことをブロックしている”というわけです。code)”べきだからです。実際、Node.jsはシングルスレッドです。–これらの理由から、ブロッキング操作は極力避け、かわりにノンブロッキング操作を行うべきなのです。関数を他の関数に渡してコールバック処理を行う必要があります(例えば、10秒待つ、データベースに問い合わせる、大変な計算をするときなどです)。“ねえprobablyExpensiveFunction()(時間がかかりそうな関数)さん、担当の仕事をして下さい。私Node.jsはシングルスレッドだから、あなたの仕事が終わるのを待ってられないんだ。loopを読んでみて下さい。)では、なぜ我々のアプリケーションの”リクエストハンドラによるレスポンスのしかた”ではノンブロック操作がうまくできないのでしょうか。今一度、アプリケーションを修正して問題を直接再現してみましょう。一度startリクエストハンドラを使います。upload;おわかりのとおり、新しいNode.jsモジュールchild_processを使っています。これのおかげで、とてもシンプルで便利なノンブロッキング操作、exec()を活用できるのです。exec()は、Node.jsの中からシェルコマンドを実行します。ユーザがURL/startをリクエストするとこのリストがブラウザに表示されます。-lah"を実行してその結果を変数に格納し、返すだけです。いつもどおり、アプリケーションを起動して、http://localhost:8888/startにアクセスしてみましょう。したWebページがロードされ、文字列”empty”が表示されます。おや、なにか間違っているのでしょうか?ご想像の通りかもしれません。exec()はノンブロッキングなやりかたで魔法を使ったのです。良いでしょう、これでとても時間がかかるシェル操作を実行できるのですから(例えば、巨大なファイルをコピーしまくるとか)。sleepの時のように、ブロッキングされてアプリケーションが完全に止まってしまうことはないのです。/”みたいなものと入れ替えてみて下さい)しかし、このエレガントなノンブロッキング操作、あまり嬉しくないですよね。ブラウザが結果を表示してくれないわけですから。?じゃあ、直していきましょう。直しながら、なぜこのアーキテクチャがうまくいかないのか解明しましょう。問題はexec()です。ノンブロッキングで動作するよう、コールバック関数を活用します。先ほどの例では、exec()関数を呼び出す時の第2引数として渡されるのは匿名関数です。content;の実行へ進んでいます。この時exec()は非同期式に動作するため、exec()に渡されたコールバック関数が呼び出されておらず、contentはまだ”empty”のままなのです。-lah"は(ディレクトリ内にファイルが数百万もない限り)それほど時間がかからず素早く実行されます。それはコールバックが比較的早く呼び出されるからですが、それでも非同期に実行されます。/”と実行すると、私のマシンではおよそ1分程度かかります。URLを開いたとき、すぐにHTTPレスポンスが返ってきます。exec()はバックグラウンドで実行され、Node.jsはアプリケーションの実行を続けるということがわかります。/”コマンドの実行が完了した時にだけ呼ばれるであろうことが想像できます。これでどうやって目的を達成できるのでしょうか。ユーザに対してカレントディレクトリのファイル一覧を表示したいのです。次はどのようにリクエストハンドラがブラウザに正しく応答させられるかを考えていきましょう。ノンブロッキング操作で応答するリクエストハンドラ“正しく”というフレーズを使いました。ひとつの”正しい方法”なんていうものはないですから。良い方法ならあります。Node.jsでよく見かけるように、関数を渡し合う方法です。今のところ、アプリケーションは(リクエストハンドラがユーザに表示したい)コンテンツを送ってやることはできます。リクエストハンドラからHTTPサーバへ、アプリケーションのレイヤーをまたがって(リクエストハンドラ->ルータ->サーバと)返していくわけです。新しいアプローチ方法はこうです:サーバにコンテンツを渡すのではなく、サーバをコンテンツに渡します。正確にいうと、ルータを通して(サーバのコールバック関数onRequest()から受け取った)responseオブジェクトを、リクエストハンドラに渡してしまいます。それにより、このオブジェクトの関数を使ってリクエストハンドラが自分でリクエストに応答することができます。解説はもういいとして、アプリケーションをステップバイステップで修正していきましょう。第3引数としてresponseオブジェクトを渡しています。responseオブジェクトのメソッドは一切呼び出していません。これはrouteにすべて任せようと考えているからです。リクエストハンドラからの戻り値を受け取らず、responseオブジェクトを渡します。リクエストハンドラがない場合、適切に”404”ヘッダとボディーを自分で返します。upload;リクエストハンドラ関数は、リクエストに直接応答するためにレスポンスのパラメータを受け取って使用する必要があります。startハンドラでは、exec()の匿名コールバック関数の中から応答しています。Upload”と応答すること自体は同じですが、responseオブジェクトを使って応答するようにしました。index.jsとして)アプリケーションを起動しましょう。期待通り動くはずです。upload;これでhttp://localhost:8888/startへのHTTPリクエストは最低でも10秒間かかることになりますが、/startがまだ終わっていなくても、http://localhost:8888/uploadへのリクエストに対する応答はすぐに返ってくるはずです。有益なものを提供するここまでのところ、まことに結構な感じですが、まだお客さんにとって価値のある賞を獲得するようなものは何も作っていません。サーバ、ルータ、リクエストハンドラはここにあります。コンテンツをサイトに追加して、ユーザがファイルを選択、アップロードし、アップロード済みのファイルをブラウザで表示できるようにしていきましょう。ここではシンプルにするため、画像ファイルだけアップロードおよび表示できるようにしたいと思います。OK、ステップバイステップでいきましょう。ここまででJavaScriptのテクニックや原則は説明してきましたので、すこし飛ばしていきます。自分が話しすぎるのを聞いているのが好きみたいですね。ステップバイステップと言いましたが、これには2つの意味があります。1つ目はPOSTリクエストを取り扱う方法、そして2つ目はアップロード処理にNode.jsの外部モジュールを利用する方法です。このアプローチ方法を選んだのには2つの理由があります。通常、POSTリクエストはNode.jsでは比較的シンプルに取り扱うことができます。練習する価値は十分にあります。外部モジュールを使う方法自体は初心者向けチュートリアルに入れる内容として意味があると考えたからです。textareaを用意して、ユーザが入力し、サーバにPOSTリクエストで送信するようにします。このリクエストを受け取って取り扱い、textareaの内容を表示してみます。upload;これがウェビー賞を受賞できないなら、他にどんなものが受賞できるのか皆目検討もつきません。http://localhost:8888/startにブラウザでアクセスすれば、シンプルなフォームが表示されます。されないとしたら、アプリケーションの再起動を忘れているのでしょうね。リクエストハンドラにビューの内容を書くなんて酷い。ここでは追加の(例えばビューをコントローラロジックから分離する、といったような)抽象レイヤーを挟むのはやめました。それをやってもJavaScriptやNode.jsのコンテキストの理解を深められるわけではないですから。それよりももっと面白い課題に取り組むことにしましょう。ユーザがこのフォームを送信すると/uploadリクエストハンドラにたどり着く、POSTリクエストを扱う部分です。初心者から脱していますので、POSTデータの扱いがノンブロッキングであることには驚くことはないでしょう。非同期のコールバックを使えば良いわけです。これは至極真っ当な動作ですね。–これで、ユーザが何メガバイトものサイズのテキストを入力しても、何ら問題ありません。すべてのデータを一つの塊として扱うので、ブロッキング操作となってしまう可能性もあるのです。プロセス全体がノンブロッキングで動作できるよう、Node.jsは特定のイベントの時に呼び出されるコールバックではコード上でPOSTデータを細かい塊で扱えるようにしてくれます。特定のイベントとは、data(POSTデータの中の新しい塊が届いた時)とend(すべての塊を受信した時)です。Node.jsがこれらのイベント発生時にコールバックするために使う関数を指定してやらなければいけません。これは、HTTPリクエストを受信するときにonRequestコールバックに渡されるrequestオブジェクトにリスナー(listeners)を追加することで実現できます。received});このロジックを実装するとき、疑問が湧いてきます。–つまりresponseオブジェクトでやってきたように、ルータやリクエストハンドラにrequestオブジェクトを渡していません。私が思うに、リクエストから全てのデータをアプリケーションに渡すのはHTTPサーバの仕事なのです。最終的なデータをルータやリクエストハンドラに渡すのです。ルータやリクエストハンドラがそれをどう扱うかを決めれば良いのです。dataおよびendイベントコールバックはサーバ内に置いておき、dataコールバック内ですべてのPOSTデータの断片を集めて、endイベントが呼ばれたときにルータを呼出すのです。ルータに集めたデータの塊を渡せば、そのままリクエストハンドラに渡されることになります。次に新しいPOSTデータの塊を受信する度にpostData変数に追記していく”data”イベントリスナーを追加、そして全てのPOSTデータが収集できた時にだけ呼び出されるよう、ルータ呼び出し部分をendイベントコールバック内に移動しました。POSTデータは後ほどリクエストハンドラ内で必要になるため、ここでは一旦ルータに渡しています。一つデータの塊を受け取る度にコンソールでログ記録するのはよろしくありません(何メガバイトものPOSTデータですからね?)。今回に限っては、何が起きているか確認してみましょう。ここでは、textareaにテキストをたくさん入れたり少しだけ入れたりして遊んでみると良いと思います。多めのテキストを入れた時にはdataコールバックが何度も呼ばれることがわかるはずです。アプリケーションをもう少しかっこ良くすることにしましょう。/uploadページで、受け取った内容を表示します。これを実現するためには、postDataをリクエストハンドラに渡す必要があります。upload;これでよし、リクエストハンドラでPOSTデータを受信できるようになりました。このトピックでひとつやり残したことがあります:ルータに、そしてリクエストハンドラに渡したのはPOSTリクエストのボディー全体です。POSTデータの個々のフィールド、つまりこのケースにおけるtextフィールドの値を消化したいですね。querystringモジュールについてはすでに読んだ通りです。upload;さて、初心者向けチュートリアルとしてのPOSTデータの扱い方はこれで全部です。ファイルアップロードの扱いではこのユースケースも終盤となりました。ブラウザでユーザに画像ファイルをアップロードさせて、アップロードされた画像を表示するという計画でした。今日ではあと2つのことを満たさなければいけません:Node.js外部ライブラリをインストールする方法、そしてそれらを私たちのコード内で利用する方法です。Geisendörferによるnode-formidableモジュールです。このモジュールは受け取ったファイルデータの面倒な処理をうまく抽象化してくれます。最終的にはファイルを受信する部分は単にPOSTデータを取り扱えば良いだけになります。-細かいところを見れば、たしかに悪魔が存在します。出来合いのソリューションを使うのはとても有用なことなのです。関連するNode.jsモジュールがインストールされている必要があります。Node.jsにはNPMというパッケージマネージャが同梱されています。これのおかげで外部のNode.jsモジュールを非常に簡単に使うことができます。POSTで送られたフォームデータはNode.jsでパースすることができるようになります。あとは送信されたフォームが抽象化された新たなIncomingFormを作成するだけです。これでフォームを通して送信されたフィールドやファイルといったHTTPサーバのrequestオブジェクトをパースするのに使うことができます。);}).listen(8888);このコードを書いてnodeで実行すれば、ファイルアップロードを含む簡単なフォームを送信することができます。}このユースケースを完了するためには、formidableでのフォームのパースロジックを我々のコードに組み込み、アップロードされた(/tmpフォルダに保存される)ファイルの内容を、リクエスト元のブラウザへ返してやる必要があります。ローカルのハードドライブに画像ファイルがあったら、リクエスト元のブラウザに対してどうやって返しますか?このファイルの中身を読み込んでNode.jsサーバに渡すというのはわかりますね。驚くなかれ、まさにそのためのモジュール、fsモジュールというものがあります。/showというURLのためのリクエストハンドラを追加し、ハードコーディングで/tmp/test.pngというファイルの内容を表示してみます。その場所に本物のpng画像を保存しておく必要があります。show;この新しいリクエストハンドラを、/showというURLにマッピングします。handle);サーバを再起動してhttp://localhost:8888/showをブラウザで開けば、/tmp/test.pngとして保存されている画像ファイルが表示されるはずです。あとやるべきことは/startで、ファイルアップロードのHTML要素をフォームに追加する/tmp/test.pngにアップロードされたファイルを保存するように、node-formidableをuploadリクエストハンドラに組み込むアップロードされた画像を/uploadURLのHTML出力結果として組み込むステップ1は簡単です。multipart/form-dataのエンコーディングタイプをHTMLフォームに追記し、textareaを削除、ファイルアップロードのinputフィールドを追加して、submitボタンのテキストを”ファイルをアップロード”に変更すれば良いだけです。show;これだけです。次のステップは少しだけ難しくなります。uploadリクエストハンドラの中で、ファイルアップロード処理を行いたいのですが、そこで、requestオブジェクトをnode-formidableのform.parseに渡してやる必要があるということです。ここで私たちが持っているのは、responseオブジェクトとpostData配列だけです。なんと残念なことでしょう。requestオブジェクトをなんとかしてサーバからルータへ、そしてリクエストハンドラへと渡してやらないといけないようです。綺麗なやり方がありそうですが、とりあえずこのアプローチ方法でなんとかなりそうなので、今回はこれでよしとしましょう。同様にこれらのイベントを消化する必要があるform.parseでそのデータを受信できなくなってしまうからです(Node.jsはデータをバッファリングしてくれないのです)。route;これで、requestオブジェクトはuploadリクエストハンドラ関数で使うことができます。node-formidableではアップロードされたファイルを/tmpのローカルファイルとして保存する時の細かい処理を受け持ってくれるので、このファイルがちゃんと/tmp/test.pngという名前にリネームされるところを確認する必要があります。ここでは簡単に済ますため、画像はすべてPNGであると想定しています。リネームの際のロジックは少しだけ複雑です:Windowsにおけるnodeの実装では、既存のファイルの位置にリネームすることができないため、エラー発生時にはファイルを削除する必要があります。show;これで完了です。サーバを再起動すれば、このユースケースが実現できているはずです。ハードドライブからローカルのPNG画像を選択、サーバにアップロードすれば、Webページに表示されるでしょう。まとめと展望おめでとうございます、ミッションはこれで完了です!シンプルだけど一人前のNode.jsのWebアプリケーションができました。関数型プログラミング、ブロッキングとノンブロッキング、コールバック、イベント、カスタムモジュールと内部、外部モジュール、その他諸々について学びました。やってデータベースに接続するのか、どうやって単体テストを書くのか、どうやって外部モジュールを作ってNPM経由でインストールできるようにするのか、GETリクエストをどうやって扱うのか、といったような簡単なことも触れていません。初心者向けの本としてはこれが運命なのでしょう。すべてのことについて細かく完璧に説明することはできないのです。良いニュースがあります。落ち着きのない子供を想像してみて下さい)、たくさんのリソースがあります。あなたの質問に応えてくれるところも沢山あります。directoryあたりからはじめると良いでしょう。now"これはNodeの素晴らしい入門の手引きです。Dahl"正確で的を射ていて、それでいて読んでいて楽しい。私はnodebeginner.orgが好きです。Adzic"いままで読んだ中でも、最高のチュートリアルです。Javaコーダーとして、いつもJavaScriptはブラックアートのように思っていましたが、このチュートリアルのおかげでとても簡単に思えるようになりました。"Erskine、コメントより"とてもよく書かれていて、""あまりうまくかかれているものだから、私が一気に全部読んでしまった数少ない記事のひとつです。Gibler、コメントより"必須。ライセンスに従うものとします。