Redux(react-redux)における適切な配列要素の更新
2021年8月23日…
最近、Node.jsのコーディングには積極的にES6の記法を用いている。特にその中で見通しの良さに貢献していると思うのが、Promiseの導入だ(Generatorはまだ使えていない)。Promiseの導入では恩恵が多く、コールバック地獄のように、どんどん右寄りにコードが連なっていく状態にはならないし、簡単に非同期処理を同期処理のようにハンドリングできる。Asyncモジュールの使用感の比較はしていないが、Githubでのスター数、比較的新しい非同期処理制御についての記事を参照した結果、Bluebirdを使用することにした(※2016年5月28日現在)。ちなみに、他の候補であったモジュールは下記である。表組みには、Githubのスター数も表示しているので、念のためBluebirdも入れておく。
モジュールの名称 | Githubでのスター数 | URL | Promise, Generatorの対応状況 |
Bluebird | 11,136 | https://github.com/petkaantonov/bluebird | Promise, Generator |
Async | 17,847 | https://github.com/caolan/async | Promise |
Q | 11,708 | https://github.com/kriskowal/q | Promise |
co | 5,498 | https://github.com/tj/co | Generator |
各モジュールの特徴だが、全部を把握しているわけではないので、知っている範囲のことをコメントしておく。まず、Bluebirdは、上記4つの中で最も開発開始時期が新しいモジュールだ。機能自体の特徴としては、PromisifyAllというメソッドにより、ロードしたモジュールをPromise化することができる、魔法っぽいメソッドを持っている。Asyncはこの中で最も歴史の長いモジュールで、インターネットにも知見が多い。機能的な特徴は把握できていない。Qはこの中では、Asyncに続き2番目に歴史の長いモジュールだ。Async、Qはドキュメントを流し読みしてみただけだが、どうやらPromiseにのみ対応しているようだ。最後に、coだがこの中では2番目に新しいモジュールで、ECMAScript 2015のGeneratorにフォーカスしている。ちなみに、このcoモジュールはNode.jsで最もポピュラーなWebアプリケーションフレームワークのExpressの開発者であるtjさんが開発したものである。
Bluebirdを導入してハマったことは下記の2点だ。
どちらもおそらくドキュメントを熟読していれば回避できたのかもしれないが、ハマってしまった。とりあえず、前述の2点について順に説明することにする。
BluebirdのPromise化が有効化されない場合であるが、これはとても簡単な問題である。Node.jsのコールバック関数の書き方には決まりがあるが、それに準拠せずにコールバック関数を記述した場合に起こる。BluebirdのPromise化が有効なのはNode.jsスタイルのコールバック関数のみ下記のような形式が正しいNode.jsのコールバック関数の宣言となる。
var nodeStyle = function(data, callback) { //Do something... //Example code var result = 'Callback from nodeStyle-function.'; callback(0, result); };
Node.jsスタイルのコールバック関数はreturnする代わりに、指定したコールバック関数を呼び出す。上記コードでは関数nodeStyleの第二引数であるcallbackが呼び出す対象のコールバック関数となっている。よって、この関数ではreturnしないで末尾で関数callbackが呼び出されて処理が終了している。Node.jsのコールバックの特徴として、callbackの第一引数がエラーが入り、第二引数に返し値が入る。ここではエラーが発生しないという想定で、関数callbackの第一引数には0を入れている(0以外ではNULLなど)。第二引数には、関数の処理結果であるresultが入っている。よって、この形式を守らずに、例えばreturnをしている関数だとBluebirdのPromisifyは動作しない。
これもとても簡単な問題で、これについては完全にドキュメントをよく読んでいないせいだった。PromisifyAllが有効化されない、ではなく、PromisifyAllが有効化されていても気づかなかった、という表現のほうが正しい。これは簡単で、対象の関数についてPromisifyAllをすると、すべてのメソッドの末尾にAsycとついたメソッドがあらたに追加される、ということだ。Bluebird公式ドキュメントのPromisifyAllのページにはしっかりそのように記述がある。
Promisifies the entire object by going through the object’s properties and creating an async equivalent of each function on the object and its prototype chain. The promisified method name will be the original method name suffixed with
suffix
(default is"Async"
).
これを知らずして、もとのメソッドが変更対象だという頭でいると、もとのメソッドを呼び出してエラーを見ることになるが、知っていればsuffix付きのメソッドを呼び出してPromise化されたメソッドを使うことができるというだけだ。デフォルトのsuffixがAsyncだが、これは変更可能ということである。
Bluebirdについては情報自体が日本語文献では少ないので、英語の読解力が試されるところではあるが、やはり公式ドキュメントが一番重要だというのが分かる出来事であった。英語は臆せず読むことが大切だ。
正しい方法でpromisifyAllしても、モジュールによっては何らかの原因で正常動作しないこともある。例えば、pg(node-postgres)というモジュールをpromisifyAllしても、done()という関数、これはおそらくNoded.jsスタイルでないがために、他のメソッドがPromisifyされたとしてもdone()というかなり重要な関数が認識されなくなる(pgのバージョンは4.5.6)。よって、このpgをpromisifyAllすると単にそれをしただけでは一部関数が正常動作しなくなるということだ。promisifyAllは便利だが万能ではない。
僕が真面目に非同期処理の制御をしようと取り組み出したのは、下記の記事を書いた友人の伊藤くんが「今僕が書いてるコードにコールバックは一つもないね」と言っていて、すげー!となったから。ECMAScript 2015初学者にとってはとても参考になる内容だと思うので、ECMAScript 2015はどんなものかという人はまず読んでみると良いと思う。
ES6時代のNode.js – Yahoo! JAPAN Tech Blog
プログラムを書きながらTranceを聴くのが良いですね。みなさんも聴いたほうがいいですよ、Trance。EDMよりハードトランスでしょ。
Discussion about this post