node.jsをひと通り使ってみて

普段C#とかPythonを打ってる私がある日突然node.js+Express(ejs)でWebSocketで通信するシステムを作れ、画面はjQueryで更新しろとか言われて、まあ、ひと通り開発してみた感想を述べてみる。

覚えやすい

JavaScript自体は覚えやすい。オブジェクト指向だから。

ただし、オブジェクト指向と言えどもクラスベースではない。つまりクラスという概念は無い。だから最初はちょっと混乱するかもしれない。また、プロトタイプチェーンと動的にプロパティを拡張可能という概念が少しわかりにくいかもしれない。このあたりは、以下がとてもわかり易い説明だった。

継承とプロトタイプチェーン | MDN

一度覚えてしまえばあとは苦労はない。集合に対する処理も、mapやfilter、forEachといった関数が利用できるので、C#のLINQ、Pythonのリスト内包表現と同じく柔軟な書き方が可能だ。欲を言えば、ラムダ式が使いたいくらいか。

ライブラリが充実している

jQueryプラグインや、jQueryベースのライブラリがたくさんある。面倒くさい処理は、きっと必ず誰かが実装してくれている。これは高い生産性につながる。

socket.ioもかなり使いやすい。イベントで識別する通信。expressと組み合わせればもっと簡単。.of('/')と書けば、ルートのページでWebSocketがつながったとき、という意味になる。やりたい事だけをうまく書けている気がする。

まあ、jQueryやsocket.ioは別にnode.jsじゃなくても良いのですが・・・。

フレームワークが使いやすい

近代的なWebフレームワークの例にもれず、高機能なパッケージマネージャが付いている。コマンド一つで何でもかんでもやってくれて、しかも、デプロイはフォルダごとコピーで良い。依存関係の記述もパッケージインストールと同時にやってくれる。

コールバック地獄

Node.jsはI/O周りの関数の殆どを非同期で処理する。非同期で処理するので、実行時にコールバック関数を要求する。で、時間のかかるI/Oが終わったらそのコールバック関数が実行される。

このコールバック関数を取るというのが曲者なんだ。たとえば、

  1. ファイルを開いて何かを読み出す
  2. その内容を元にHTTPリクエストを投げて結果を貰う
  3. 別のファイルに結果を書き出す

という単純な処理を書こうとすると、これらそれぞれに非同期で実行するためのコールバック関数が必要になるので、少なくともネストが3階層になってしまう。これにtry-catchとか、通常の制御構文などを付け加えると、あっという間にネストは深くなってしまう。

まあ、コールバック関数を引数として与えるところで初めて定義する必要もないので、一旦関数オブジェクトを変数に入れてあげれば見栄えは良くなる。けれどもそういうことはみんな考えるのであって、こういう処理を簡潔に済ませるためにasyncなどのライブラリが提供されている。色んな所でIOが必要になる場面では必須と言ってもいいだろう。

ちなみに、C# 5.0ではasync/awaitという構文があって、こういうことをやらなくても良いようになっている。(余談だが、この構文、実装が少し変で、Taskクラスに全面的に依存している。ただでさえ、C#の並列処理はこれまでやり方がコロコロ変わっている。もしTaskクラスが使えなくなったらどうするんだろう?・・・と思ったら、同じ趣旨のことを言っている人が居た。

結論

まあ、いいんじゃないでしょうか。ただ、巷で言われているほどすごいかというと、なんかそれほどでもないような気がします。

そもそもJavaScriptがあんまり使いやすいとは言えないような気がします。少なくとも、他の言語に比べていて、言語仕様のレベルで優れているとまでは言えないと思います。簡単なプログラムをそれほど時間をかけず、パパっと作っちゃうということには長けていますが、業務アプリケーションなどで複雑なビジネスルールを実装するにはちょっとキツイ気がします。

それに、簡単なプログラムを時間を書けずにぱぱっと作るだけなら、別にnode.jsじゃなくても、pythonでもrubyでもいいわけです。

じゃあnode.jsであると何が嬉しいかというと、何だったんでしょう。もう一度振り返ってみます。私は以前の記事で、IBMの記事がnode.jsを理解するのにすごいわかりやすかったと書きました。

これをもう一度読んでみます。Node.jsの第一の目標は、「スケーラブルなネットワーク・プログラムを作成するための簡単な方法を提供すること」であったと書いています。なるほど、そのとおりです。子プロセスを作ったり、スレッドを作るモデルでは、時に資源の無駄使いが発生します。

じゃあ具体的にどんな時、node.jsだと嬉しいかというと、「RESTful な API」「Twitter のキュー」「テレビ・ゲームの統計データ」のようなアプリケーションを挙げています。つまり、単純明快な処理だけど、要求の数が膨大になってしまうような事例です。

確かにその通りだと思います。そして、やっぱり一般の業務アプリを構築したりするような用途ではないと思います。

Node.jsはそれ単体でスケーラビリティを解決する魔法でもないし、Node.jsだけを使って複雑な業務アプリケーションを実装するのも厳しいでしょう。しかしながら、上記のような用途ではNode.jsがとっても頼りになるのは確実です。頼りになりますが、注意して実装しないとスケーラブルにもならなかったりします。

このように、メリット、デメリットあるのは物事の常なのですが、なんだか世間ではNode.jsがもてはやされすぎではないでしょうか。

そして、我が社の某プロジェクトでは、とっても大規模なシステムのすべてをNode.jsにしてしまうことが決まってしまいました。私はこの試みは絶対に失敗すると思っています。このシステムでは、厳格に決められた時間で周期起動するような処理もあれば、C言語で作成されたレガシーなプログラムと通信する場合もありますし、帳票を作る機能も、バイナリデータを高速かつ連続的に処理する必要もあれば、GTKやMFCやWindows Formsで実装されたUIとやりとりしなければならない場面もあります。これらすべてを扱うベストの選択肢がNode.jsだとは到底おもえません。いや、何の言語を採用しても、単一の言語ですべてを実装しようとする限り、たぶん失敗するでしょう。

何でも解決する魔法の技術というのはありません。狼を打ち倒す銀の銃弾は無い、と大昔から言われていました。なのに、こういうことが未だに起こってしまうのはなんででしょうか。大規模なシステムであれば、機能に応じて適切な実装を選ぶ余地があっていいはずでしょう。

なんだかまた話がそれてきたのでここらへんでやめておきます。簡潔にまとめると、「Node.jsは良いところも悪いところもあるよ。それを理解してNode.jsを使えばとっても便利だよ」という至極当たり前の結論になると思います。