Kanasan.JS JSDeferred コードリーディングに参加した

06/07(日)に大阪でKanasan.JS JSDeferred コードリーディングが開かれた(告知ページ)。JSDeferredってのは非同期処理を行うためのJavaScriptライブラリ。コードリーディング自体が初めてで何を準備していいか分からなかったので、とりあえずソースコードだけダウンロードして参加した。

概要

最初にJSDeferredドキュメントサンプルコードをざっと読んだ。ふむふむ。なんとなくわかった、細かいとこはソースコードを読めば分かるだろう、って流れ。

んで、いよいよソースコード本体に突入。素直に上から読み始めた。

コードの内容

というわけで読んでみる。

...あの、超ムズいんですけど。

JSDeferred はコードが短い。全体で400行、コメントを除いたら211行。それにサンプルも結構あるので割と読みやすいと思う

Kanasan.JS JSDeferred コードリーディングが開かれる - ess sup

誰だよ、こんなこと言ってたのは(というか僕だよ)。「どこがだよ!」と問い詰めてやりたい。

分かったこと

はっきり言って非同期処理の部分についてはほとんど分からなかった。それでもJavaScript一般について分かったことがあったので書いておく。

配列っぽいオブジェクトをほんとの配列にする。

配列っぽいオブジェクトをほんとの配列に変換するイディオム。次の条件を両方満たすオブジェクトは、 Array.prototype.slice を使えば1行で配列に変換できる。

  • オブジェクトのプロパティが全て数値である
  • lengthプロパティが定義され、その値が要素数に等しい

具体的なコード例は以下。

// jsdeferred.js 160行目
values.length = dl.length;
values = Array.prototype.slice.call(values, 0);
// jsdeferred.js 286行目
var args = Array.prototype.slice.call(arguments, 1);

特に1つ目の例は手動でlengthプロパティを追加していてテクニカル。

クロージャーをつかってループ変数を閉じこめる例
// jsdeferred.js 155行目
for (var i in dl) {
    if (dl.hasOwnProperty(i)) {
        (function (d, i) {
           // 省略 ...
           // d.next(function (v) { とか
        })(dl[i], i);
    }
}

if文の中で無名関数を定義してスコープを区切ってる所は、この場面では必須のテクニック。for内にあるd.nextでは setTimeout を使っているので、ループ変数iはこのfor文が全部終わった後にも参照される。なので、こうやってfunctionでループ変数iを各ループごとに固定しておかないといけない。そうしないとループの最後のiの値が使われてしまう。

感想とか

素直に上から読んでいったけど、関数を読む順番を工夫した方が良かったかもしれない。まず最初のDeferredオブジェクトの定義が抽象的で、その次のDeferred.parallel関数もかなり難しい。誰かが「Deferred.next_default関数から先に読んだ方がいいんじゃないの」って言ってくれて、それで少し読み取りやすくなった。

あとはもうちょっと簡単な例があれば良かったと思う。jsdeferred.js 268行目にある「2の10乗を非同期で求める」例は、僕には複雑すぎて意味がつかめなかった。ちなみにこの例は、ドキュメントでは「Basic Chain」って書いてある。なんてこった。

というわけで、JSDeferredがやっとわかった - by edvakf in hatenaを参考にして復習してみよう。コールバックがDeferredオブジェクトを返すかどうかでチェーンの処理が変わる、ってのがポイントだね。

補足

読んでいて「この部分意味無いんじゃないの」って意見が出たとこがあった。で、今日見てみたらid:nanto_viさんが修正してコミットしてた(そのコミットのdiff)。ありがたい。