qsonaのブログ

プログラマーです。Node.jsでのサーバサイド開発の国内事例を増やすのが目標。

息子が「ふしぎな国のアリス」の絵本を読んでいたので、あらすじを聞いてみた

4歳8ヶ月の息子が、実家にあった「ふしぎな国のアリス」を読んでいたので、読み終わった後に試しにどんな話だったか聞いてみた。本をペラペラめくりながら話してくれた。それを記録として残す。(息子は、文字や文を読むことに相当興味が寄ってるので、年齢にしてはかなり読める方だと思う)

本はこれ。 https://www.amazon.co.jp/dp/4591021645 ひらがなで3000字くらい、結構長い。見開きで、150字程度ごとに絵がある。


まず穴に落ちて、そして、入り口がせまかったからえんえん泣いて、そしたら池になっちゃった。池に浮かんで、アリスはまたそとに出て、おうちにかえったら手袋がなくて、ジュースを飲んだら大きくなって、きのこを食べたら首が長くなっちゃった。そしてね、ねこに言われて、そして、おうちはどこ?ってきかれておうち行けたから早く行きなさいって言われたからお茶もジュースもないわってアリスがいって、お茶を飲もうとしてもコップはからでした。しばらく帰ってなんか行って、しばらく行くとトランプの職人が白いばらの花を赤いペンキで塗っていました。「女王様が白いバラが嫌いです」っていったから、ここ通って見つかって、ににん(二人)の首をきって、アリスは切ってないけど、この塗ったやつの首が切れた。女王様はまけて、女王がもってる棒を渡して、フラミンゴを渡して、フラミンゴをだーんだーんってやって、女王様は負けたのね。アリスが勝ったんだけど女王様は大喜びだったのね。アリスが勝ったら死刑になっていたでしょう。地上に帰る道をわからなかったから教えて、グリムフォンにたずねなさいって言ったからグリムフォンにきくと、にせうみがめにたずねなさいって言って、にせうめがみに聞くとそれはえびにたずねなさいって言って、えびにきくとそれは裁判官に尋ねなさいっていって、そうすると検事が帰ってきた。アリスは王様のランドにいって、ハートのジャックが女王のパイを盗んだ罪で裁判がおこなってたんだって。帽子屋と3月うさぎが立って、私たちがパンを食べていました、女王は判決を言ってハートのジャックは死刑にして、アリスはあきれて大声で「そんな簡単な理由で人を死刑にするなんて許せないわ」というと突然アリスの大きくなりました。命令すると、トランプの人がね、やりを構えたんだって。そしてアリスにおそいかかったら、アリスをよけなさいっていって、落ち葉をはらってねごとを言ってるけど、どんな夢をみてるのってお姉さんに言われるとやっと目をさましたんだって。


明らかな誤読は、実際にはトランプの職人の首は切れてない(「アリスが隠して、刀に赤いペンキを塗って、女王をだました」のくだりが多分理解できなかったっぽい)ことと、アリスと女王様の勝ち負けが逆転してることの2点。適度に飛ばしながらスラスラ喋るので、ある程度頭に内容が入っているのだろうと思う。

子供が話すことをテキストで残すのは、成長の記録としていいかもしれない。

第65回 HTML5とか勉強会 ー React最新情報 に参加したメモ

リンク

公式

eventdots.jp

安定のりぃさんログ

lealog.hateblo.jp

togetter

togetter.com

もう十分まとまってた。

感想

自分の立ち位置としては、フロントエンジニアではないけどJavaScript自体には詳しい方で、社内でReact/Reduxで動いてるPJがあるのもあって興味はある(いずれ書けるようになる必要がある)。Reactやfluxが何なのか的なのは理解するよう努力している。最新情報を普段から追うのは難しい。CSSは書いたことない。という感じでしたが、どのトピックも非常にストレートに自分のためになりました。

各セッション

React現状確認 by @koba04

speakerdeck.com

Reactの最新情報盛りだくさん。今後非推奨になる項目は特にチェックしておかなければです。

具体的なところだとPropTypesが非推奨っぽくなる話が特に気になった。flowやTypeScript使う、あるいはPropTypesは別パッケージへ。

なぜReduxを使うのか by @kuy

speakerdeck.com

Storeの役割を分割して名前をつけた、というのには納得。

Reduxの役割は状態管理に特化していて、外れるところがmiddlewareに切り出されエコシステムになっているというのは良い面でありつつ、・・・

スライド中にも入っている以下の議論。

togetter.com

このあたりについて、懇親会でkuyさんと古川会長と話すことができた。

フロントエンドの役割をもつサーバを挟み(BFF: Backends for Frontends などと呼ばれる) middlewareでやっているような処理はそこで持たせる、というようなことを話されていた。ちょうど、フロントサーバを導入したときにそういうフロントのための処理をどの程度やるべきなのか、ということを社内でも話していたところ。

Relay by @hokaccha

speakerdeck.com

GraphQL を中心として、サーバ・フロントまで含めたフルスタックなフレームワークとして期待される。データの取得を宣言的に書ける、というメリットは確かに納得。

下の記事のように、自分はちょうど社内でフロントエンドサーバーを絶賛検討中で、GraphQLも検討中。フルスクラッチでは書けないのでRelayを導入するのは辛そうですが、考え方等は勉強になりました。

qiita.com

How to style React components by @Quramy

How to style React components

CSSは今までに10行くらいしか書いたことがなく、ちょうどCSSの構造化の話に興味があったところだったので、有難すぎる話でした。

経験なさすぎて大した感想を述べられないのが残念ですが、CSS in modulesが良さそう(小並感

今回BEMは触れられませんでしたが、こういうのもありますよね。

GitHub - axross/bemmer: BEM-like simple classname builder.

まだ標準がないだけに色々な苦労がありそう(小並感

Atomic Design powered by React @ AbemaTV by @ygoto3_

www.slideshare.net

Atomic Designについては初めて知った。実例では、思った以上に本当に細かい粒度に分けていたのが印象的。

Stateless Componentでかつ粒度が小さく、ロジックがほぼ含まれないものであれば、デザイナーにとってもjsxを書く敷居が低くなる。確かに。

こちらの記事も参考になる。

ygoto3.com

その他

Reactの勉強会でしたが、AngularJSで有名な金井さんが受付のお手伝いをされていた。実は3年前くらいに本当に右も左も分からなかった時代、初めて行った勉強会はなぜかAngularJSのもので、金井さんがお話しされてたのでとてもよく覚えています、という話を金井さんにしたら、烏龍茶2Lを頂きました。

ygoto3さんは僕の前職であるサイバーエージェントの方で、チームの方々といらっしゃっていた。そのうちの1人は同期で、久しぶりに会った。技術的にもチームメンバーにも恵まれて、楽しそうで良かったなと思いました。

まちがってもいいんだよ、の言葉が返ってきた話

夜中だけど不思議な気分で寝られなくなったので、そのことについてポエムを書く。

息子(3歳9ヶ月)、結構ミスを気にする慎重派で、
親の前ではやるけど他の人の前ではやらないことが多かったり、
何回か「違うよ、こうやるんだよ」的な指摘をすると、その後気にしてやらなくなったりすることが多い。

少なくとも2歳になった頃にはそういう性格が見えてきてたと思う。それで息子にはことあるごとに、
「まちがってもいいんだよ」 「失敗してもいいんだよ」 と言い続けてきた。

そのうち、本人は「まちがってもいい」という言葉を気に入ったらしく、いろんな場面で使うようになった。

たまに拡大解釈して、わざと間違ったり、いわゆる悪いことをした後に「まちがってもいいんだよ」とか言い出すけど、
そんな時も極力、「間違ってもいい」こと自体は否定せずに、違う方面で理屈をつけて(間違ってもいいけどわざと間違えるのは違うとか)話すようにしてきた。




今日、僕が息子と遊んでたら、僕の力加減のせいで息子が顔をぶつけて、怪我するほどではなかったものの、
よほどショックだったのか大泣きしてしまった。
だいぶ時間が経ってなんとかその場はおさまった。

夜、息子と一緒に風呂に入った時に、ぶつけさせちゃってごめんね、痛かったよねと謝った。

息子は最初はなんのことか忘れていたような素振りだったけど、少し経ってから思い出したようで、
「いいよ」と言った。

「おとうさん、まちがっちゃったんでしょ? まちがってもいいんだよ」




子育てはすごくやりがいのあることだけれど、少なくとも乳幼児期はフィードバックが少ないから、自分がやっていることが正しいのか間違ってるのかわからないのが難しいなと思う。
例えば僕は以前塾講師を長くやっていて、その時の経験は確実に子育てにも活きているけれど、それと確実に違うと言えるのは、あの時は模試や小テストという、自分の仕事の良し悪しを調べるわかりやすい指標があったのが、今はないということだ。

そして、子育てには上司やメンターのような、自分の子供に対する接し方を見てそれに対するアドバイスをくれる人もいない。だからどうしても自己流にしかならない。

だからだと思う、思いがけず自分の言葉が逆に子供から返ってきたということに、いたく心が動かされて、そして何かすごく不思議な気分に浸っている。

Node.jsだけで2年半生きてきた自分が、そろそろRuby on Railsをやってみようと思う話

Node.jsをやってきて良かったこと

職業プログラマとして2年半、Node.jsだけをやってきた。Node.jsで特に良かったと思えるのは以下の2つかなと思っている。

OSSへの貢献

大層なことではないが、この1年で、lodash, neo-async, xto6, などいくつかOSSへの貢献ができたこと。

2年目に新規開発チームに異動し、自分がチームで最もNode.js経験が長いという状態になった。弊社はGitHubEnterpriseを利用していて、このプロジェクトではメンバ全員がForkして基本はPull Request駆動(ガチガチではないが)という、基本OSSにそのまま応用できる形でやっていて、ここでgitのコマンドもきちんと使えるようになった(2年目にして初めてgit rebaseやgit cherry-pickを使った)。

ちょうど、我々の開発中にlodash3系が出そうということで、npmではなくGitHubの最新に向けておいた。おかげでいくつかバグを踏んだりもして、issueやPRを送るのはほぼ必然だった。ユーティリティ系のライブラリだから決してハードルは高くないが、それでも大御所に貢献できるのは嬉しかったし、自身の成長を感じることもできた。それ以外にも、いろんなライブラリのソースコードを読むのは日常的だった。

趣味レベルからOSS貢献に繋げられる人はすごいなと素直に思う。でも、業務のレベルでしっかり使うと、それだけ色々な課題も見えやすいし、OSS貢献のハードルは下がると思う。OSS貢献は一つの分かりやすい技術レベル指標だと思うので、それが出来やすい環境にいたのは良かった。

プログラミングの力がついた

Railsはプログラミングじゃないみたいな記事が最近あった。それはともかくとして、Node.jsはそれ自体microだしフレームワークもその大体が薄いから、自分で考えて実装する量が多いと思う。ブラウザゲームのサーバサイドは定形処理みたいなのが少ないし、フルスタックな言語やフレームワークを生かせないという判断で、Node.jsを使ってきていたのだろうと思う。いずれにしても、課題に対して実際にコードを書いて解決するのは自分にとっても性にあっていたし、いい経験になったと思う。

あとは、非同期処理が当たり前、というのもよかった。

そろそろRailsをやりたいという話

今なら先人のフルスタックな知恵を十分理解できるんじゃないか、と思ったということ。もうだいぶ前から、サーバサイド開発といえば筆頭に上がる存在だし、今だってそうだ。それほど価値を認められているものを、このまま知らずにいるのはもったいないんじゃないかと思った。今ここでRailsを十分知ることは、Web技術者としての視野を拡げることに繋がると思っている。

これからも仕事はNode.jsだけど、一旦趣味レベルでRuby on Railsをはじめてみよう。

みちすじ

やるやる詐欺にならないように。一旦、勉強しながら、簡単なゲームをRailsで実装してみようかと。ついでに最近流行りのマイクロサービスっぽく、フロントサーバとバックエンドサーバを分離してみよう。

作るもの

7 hand pokerという、昔MSN Messengerにあったゲーム。ユニークなルールですごい好きだったんだけどMSN Messengerつかわなくなってゲーム自体もやってないから、自分が遊びたいというモチベーションがある。あと、一度対戦ゲーム(トランプとかボードゲーム的な)をちゃんと実装したいと思ってた。

構成

ブラウザ(js) <=> Node.js(リアルタイム, ルーム機能とか) => Rails(7 hand pokerのロジック+REST? API) => MySQL

作らないけど、例えばチャットからやりたかったら chat client <=> bot server => Rails => MySQL みたいな感じに、Railsからは完全にそのまま流用できるイメージ。作らないけど。

いつもいつも(プログラミングに限らず)企画だけ立派にやって完成せずに終わるので、ここに書いておけば、出来なかったらちょっとは恥ずかしい、だからがんばる、という狙い。rubyも書いたことないしrailsも起動しかしたことないので、まずは手を動かしてみる。

東京Node学園祭2015 にて発表を行いました

発表前記事

東京Node学園祭2015 にて発表を行います - qsonaのブログ

プレゼン資料・補足記事

qsona.hatenablog.com

応募まで

2010-11年ごろのピグライフでの事例以来、弊社(サイバーエージェント)では特にゲームのサーバサイド開発に積極的にNode.jsを投入してきており、サーバサイドで動かすために書かれたNode.jsのコードの量では、国内だったら弊社はかなり多いんじゃないかと思っています(一番多いまで言えるかは不明ですが)。ただ、開発の多さに比べると、発信の数が少ないと思っていて、この機会に何か発信できるものはないかと考えました。今はそんなにNode.jsの特別な使い方をしているわけではないし、特徴はコードの量なので、その中での苦労話が最もリアルに話せて価値を出せるのではないかと思い、LT枠に応募しました。

前日まで

その後、古川さんから返信を頂き、面白そうな内容なので、LTではなくセッションでちゃんと話した方が良いのではないか、という非常に有り難いご提案を頂きました。自分にとってはこれ以上ない良い経験をさせて頂ける機会で、感謝しております。さすがに背伸び感はあるものの、Node.jsのコード書いてる時間ならそうそう負けないぞという気持ちで引き受けた次第です。

1つ前のNode学園で LT をして(これも良い経験でした)、その後学園祭1週間前までは発表の大枠を練り、そこから資料作成と練習にとりかかりました。準備の時間配分のバランスが難しいのですが、30分というのは結構なボリュームで、資料の作成にもっと時間見ておかないとダメだったのと思いました(20時間くらい?)。LTの時で分かっていましたが、喋ってみると資料がしゃべりにくい順番になっていることが分かるのが結構多くて、特にトークが主体になるような発表だとトライアンドエラーで良くする必要があるので、時間はしっかりとらないなと思っています。

余談ですが、今回の招待講演以外のセッションでは特に好評を博していた id:amagitakayosi さんがかなり事前準備を一生懸命されていたようで、印象的でした。

Node学園祭で「フロントエンドに秩序を取り戻す方法」を発表した #nodefest - マルシテイアは月の上

前日夜〜当日発表まで

会場が弊社だったので前日夜に行ってみたところ、スタッフの方々がみんな集まってくださっていて、自分も資料折りと差し込みを少し手伝いました。あまりの分量で変なテンションで折っていましたが、ああいった場で少しでも普段話せない社外の方と話す機会があるのは良いですね。

※左の写真の、一番右でなんか折ってるのが自分。

準備が終わった頃、titoさん(弊社の先輩です)にプレゼン資料見せてと言われたので、発表練習付き合ってくれるならいいですよと半分冗談で言っていたら、織田さん(同じく弊社の先輩)と2人で本当にフルでの発表練習に日付変わるくらいまで付き合ってくれました。良いアドバイスをもらえたので、発表としての体裁は大分整って、当日も言いたいことが流れの中ですんなり言えたと思います。

当日、スピーカーにはお弁当を用意して頂いていて、招待講演の方々と一緒にわいわいランチ・・・する余裕もなければ英語力もなかったのですが、写真をとらせてもらったりと、良い思い出になりました。

発表の趣旨

中規模以上のサーバサイド開発にNode.jsを利用する上で、特にプログラミング上で苦労している部分や、あまり良くないことをしている部分にスポットを当てながら、問題提起をしようと考えていました。

自分のやっていることから離れたところまで話すのは難しいし、現場のリアルさに欠けてしまうので、「ゲーム開発」「ES5まで」の2つの制限の上で話すことにしました。

最近ではマイクロサービスというのが一つのキーワードになっており、適材適所に言語やフレームワークを使い分けることが可能になっていると思います。サーバサイドでは一般的にはRuby(on Rails)が多く、Node.jsの出番は特にリアルタイム性が絡む時に多い(僕の直前の @kidach1 さんの発表もそうだった)というのが自分の印象ですが、弊社の今自分が属している部門(ゲーム)では要件にかかわらず普通に、アプリケーションのメインとなるAPIサーバをNode.jsで作り始めることが多いです。

で、特に何も決め手の特徴がなければRubyあるいはPHPとなっているところに、Node.jsが入っていくことは出来ないのか、どんな壁があるのかっていうのを最近考えていて、その上では、Node.jsでのサーバ開発の辛さ的なものの共有は一つの切り口になるかなと思っていました。

資料upを遅らせた理由

発表のテーマである「バッドノウハウ」というのは、単に良くないから改善すれば良いという話ではなく、良くないとは分かっていながらも仕方ない的なニュアンスが含まれています。自分としてはそういう内容で3つ選んだのですが、そもそも解決手法を十分に考えられていないだけではないか、というご指摘を受けました。そうだとしたら単に良くないものを喧伝する真にバッドな物になるため、ご指摘を受けた部分に関しては、出来る範囲で検証してから上げること、またその後も随時指摘を頂き訂正できるよう、補足記事とセットで上げることにしました。

感想と反省点

Twitter等での反応を見る限りでは、共感を得た部分もあったとは思いますが、聴いて下さった方に十分プラスのものを与えられるようなセッションにすることが出来なかったなと思っていて、謙虚に反省しなければならない点が多かったと思います。

反省点の一つは、まだまだ考察が足りなかったこと。バッドノウハウというタイトルでしたが、ノウハウ共有系とは正反対で、現状の良くないところを見ながら問題を提起していくということで、本質的なところに迫りきれなかったのは残念なところです。

もう一つは、ES5までの話と決め打った部分に無理がありました。Node4が出てわりとすぐでの学園祭ということもあり、直asyncで書かれたコードを全部書き直すのは大変だし、既存プロジェクトでgenerators(co)導入とかはまだ難しいかなという感じでそこまで重要に思ってなかったのですが、この1日では完全に時代に置いて行かれている感じしかしませんでした。yield使えっていうツッコミも結構頂いていましたね。そもそも書き方やパフォーマンスなど諸々検証するチャンスはNode v0.11の途中からもう2年くらいあったわけで、それを怠ってきたところから反省しなければなりません。

あとはまあ動画が公開されてたので自分のものを見てみたのですが、何か気の触る話し方だなと思いましたw もうちょっと謙虚な感じに聞こえるような話し方にしたいですね!

動画はここです(すべての発表の動画があります) 東京Node学園祭2015 | 株式会社サイバーエージェント

一番人気だったkosamariさんの発表と時間が被っていましてリアルでは見れず。私も動画で見ましたが、非常に面白いプレゼンでした。

他のセッションなど

NodeDiscussionが一番面白いコンテンツだったと思いました。その場で即興で話している英語なので聴きとる難易度が高かったですが、 kosamariさんの素晴らしい逐次翻訳で全て理解することが出来ましたし。初めての試みとのことでしたが来年からも期待しています!

あとは、他の方々の発表を聞いていて思ったのは、発表の内容自体はすぐにはピンとこなかったりしても、その方がどのように考えて開発や技術選択などをしているのか、ということが節々から分かって、どれも面白かったです。

全体的にはNode.js自体というよりはその周辺技術とかJavaScriptに関係する話が多く、もっとNode.js本体に関する話も聞いてみたいなという気もしました。ドストレートにNode.jsの話だったのは、自分が聞いた中ではNodeDiscussionと @kidach1 さんのセッションだけだったかもしれない。

終了後

懇親会では色々な方とお話できて、勉強になりました。とにかく疲れがどっと出て、帰って15時間寝たあげく2日間頭痛が続いて辛かった。ものすごく問題意識が高まったり、非常に良い刺激になった1日でした。

運営の方々や登壇者の方々、スポンサーの方々、ありがとうございました!

「Node.jsでのゲームサーバ開発 愛すべきバッドノウハウ3選」の補足

本エントリは、東京Node学園祭2015でセッション発表いたしました「Node.jsでのゲームサーバ開発 愛すべきバッドノウハウ3選」についての補足記事です。

スライドはこちら:

speakerdeck.com

発表動画も上げて頂いています:

youtu.be

発表の概要

発表内容は概ね以下のようなものです。

  • 動機付け: Node.jsをサーバサイドでもっと普通に利用したい, その上での
  • バッドノウハウ1. (ES5まででの)非同期フロー制御
  • ノウハウ. マスターデータの利用
  • バッドノウハウ2. "サービス層" になんでも記述する行為
  • バッドノウハウ2.5. Dateを上書きする
  • バッドノウハウ3. process.uncaughtExceptionをlistenしてエラーを握りつぶす

現場の規模感

自分が約1年間開発に携わったプロジェクトでは、サーバサイド開発者が平均5人程度で、大体がNode.js未経験からスタートしています。普通のWebアプリケーションのサーバサイドの開発と言ってよいと思います。コードが約10万行くらい(テスト除く)。「フツウ」のサーバ開発、と形容しているのはこれをイメージしています。

BAD 1. 非同期フロー制御について

発表中、最も自分が言いたかったことを言い切っている章ですが、反応としては「yieldで解決するのでは」というものが多かったです。Node v4でのgeneratorsがサポートされ、そしてasync/awaitの文法が今後入ることがほぼ確定しており、それらを含んだ話ができずにスライドの中で「決定版が待たれる」のような曖昧な言い方で濁したのは、自分の怠慢だったと言わざるを得ません。

1年ほど前にcoを調査したときは、callbackを取る関数はthunkifyして使うものだと思っていましたが、最近見たらthunkifyはdeprecatedになり、Promisifyして使うべき、となっていました。Promiseが標準化したため、正しい選択といえますが、気分的にはthunkに比べてPromise経由はどうも遠回りに思えてしまいます。「フツウ」を標榜しているところが気にするべきことではないですが、オーバーヘッドはどうしてもthunk以上に気になるので、実コードを一部移行して検証してみようと思っています。

それと気になっているのが、非同期を同期スタイルにすることで、callbackスタイルからtry-catchのエラーハンドリングになることです。

1つは、V8のoptimizationは気にする必要がないレベルなのかどうか(場合によってはtry節を全部即時関数で囲ったほうがいいのか)ということ。

もう1つは、非同期エラーも、主にバグで出される同期エラーもいっしょくたにcatchされてしまうことになると思うので、その辺を区別する(上手な)方法はあるのか?という疑問があります。ここに関してはそもそも区別する必要がないんじゃないかという話も頂いていて、実質的にはユーザのアクセスによって到達するようなコードなのでそう問題にならないはずですが、とはいえdomainがdeprecatedになった理由なども考えると、あるべき論としては、この2つは分けて考えなければいけないのかなと思っています。

諸々検証して、中規模以上の開発で使えるプラクティスを構築できればと思います。

最後に現場での話をすると、io.jsは利用しておらず、Node v4への移行は大体出来る状態まで持ってこれた(ただし文法の移行はまだ)という状態です。varをconstやletに置き換えるなどは出来るでしょうが、asyncを利用した既存のコードが多い状態で、generatorsを利用した記法と共存させるのは規模感的にも難しいかなというイメージですが、うまく共有または移行をしていきたいと考えています。

BAD 2.5. Dateを上書きする

自作ライブラリ qsona/time-master · GitHub を紹介しましたが、 sinonjs/sinon · GitHub のFakeTimerを使うべきではないか、という指摘を懇親会で頂きました。自分もsinonをテスト上で使っていますが、このような用途で使うことは全く考えもついていなかったし、テストでFakeTimerを使ったことがなかった(ユーザのアクセス時間をアプリケーション中で使っているので、基本的にそれを書き換えていた)ので、調べてみました。

sinonのFakeTimerは、ある決まった時刻で固定する用途として使えるようです。プレゼン中では話せていませんでしたが、我々の(テスト以外の動作確認の)用途としては、ある時刻に移動させたあと、そのまま時間が流れていって欲しく、そのような機能はFakeTimerにはないようでした。

最後に

この記事は随時追記します。間違いや疑問点等あれば、ご遠慮なくコメント欄でご指摘いただければ幸いです。

async.waterfallとneo-async.angelFallの話

今回登場するモジュールはこちらです。

github.com

github.com

簡単に説明すると、Node.jsにおいて、callback地獄とif (err) return callback(err);の多用を軽減して読みやすくするためのモジュールです。

async.waterfallについて

async.waterfallは、非同期な処理を逐次行っていく時に、メインに使うメソッドです。

使い方

var async = require('async'); // or 'neo-async'
async.waterfall([
  function(next) {
    next(null, 1);
  },
  function(result, next) {
    // result に1が入る。
    next();
  }
], function(err) {
  // 途中でnext(err)すると、ここに飛んでerrが入る。
}

問題点

次のように、第2引数を返したり返さなかったりするメソッドがあったときに、その次の関数の受け方が定まらなくて、よくバグの原因になる。

function anAsyncMethod(foo, callback) {
  if (foo) {
    callback(null, 1); // [A]
  } else {
    callback(null); // [B]
  }
}

async.waterfall([
  function(next) {
    var foo = false;
    anAsyncMethod(foo, next);
  },
  function(result, next) {
    // [A] の場合はresultに値(1)が入るが、
    // [B] の場合は入らない。(すなわち、resultにnextと呼ぶべき関数が入っている)
    next(); // TypeError: next is not a function
  }
], function(err) {
});

回避方法はある(後述)ものの、面倒だし、
「値を返していない状態から、返すように変更する」ことがbreaking changeになるのも辛い。
同期関数だったり、非同期コールバックスタイルでも普通の使い方であれば、そうはならないのに。

回避方法

anAsyncMethodがコールバックへ引数渡してきたり渡さなかったりするから困るなー、でもanAsyncMethod自体は書き換えられないな、という時は、次のように回避する。

async.waterfall([
  function(next) {
    var foo = false;
    anAsyncMethod(foo, function(err, result) {
      next(err, result); // 1つ渡すことを強制している
    });
  },
  function(result, next) {
    // ...

neo-async.angelFall

waterfallと似ているが、上述の問題が解決されたもの。

常に最後の引数にnext(と呼ぶべき関数)を渡す。

すなわち、関数の仮引数を、単に(next)と書けば、前の関数が何を渡そうが無視されて、nextに関数が入る。
もし(result, next)と書けば、仮に前の関数が何も渡してなければresultにundefinedを入れ、nextに関数が入る。

var async = require('neo-async');
async.angelFall([
  function(next) {
    var foo = false;
    anAsyncMethod(foo, next);
  },
  function(result, next) {
    // 定義してる引数の個数が見られ、
    // 2つなので、1つめに前の関数から渡される結果、2つめにnextが入る。
    next();
  }
], function(err) {
});

前回の記事で紹介したFunction.lengthを利用して実現されている。

注意点

async.angelFall([
  function(next) {
    var foo = false;
    anAsyncMethod(foo, next);
  },
  function(result) {
    // nextを呼ばないから仮引数としても定義したくないよ、というとき。(実際あり得る)
    // 仮引数を1つしか定義していないので、ここにnextと呼ぶべき関数が入ってしまい、
    // 期待する値(result)が入ってこない。

他の解決案

github.com

waterfallのタスク関数はfunction(foo, next)ではなく、function(next, foo)の順で取るように仕様変更するという話。

これをすれば、fooがこようがこまいが、nextの位置はずれないので上のような問題は起きない。

実際自分もこれは1年半前くらいに思ったのだけれど、Node.jsの「コールバック関数は最後にとる」という規約に反するので少し気持ち悪い。でも黒魔術しないですむし、async.waterfallに限って言えばこれもありだなぁと思う。

まとめ

Node.jsがcallbackを最後にとるというルールが、思わぬ形で影響を及ぼしてしまっている話でした。