JSON のルーズなパーサー作りました。

設定系とかで JSON を使うときにいちいちクォーテーション(またはダブルクォーテーション)で囲むのめんどくさいなと思って、いい感じに勝手に判断してくれるやつ作りました。以下の gist に貼り付けてます。

https://gist.github.com/3258401

これを作るときに本家の json_parse を大いにパク・・・参考にさせて頂きました。
ちなみに本家は以下。
https://github.com/douglascrockford/JSON-js/

今回作ったやつは、これが

{"param1":"value1","param":"value2"}

こんな風に書けます。

{param1: value1, param: value2}

数値にできそうな奴(1234 とか .5 とか)は number に、true / false は boolean に、 null は null に、 あと標準の JSON には無い NaN とか undefined とか Infinity にも対応してます。undefined が要るのかって話は置いときますが。。

途中まではオブジェクトのキーにオブジェクトとか設定できるようにしてたんですが、それはやっぱりやり過ぎかなと思って無しにしました。

で、速度的にはどうなんやろうと思って試してみました。対象は今回作ったやつと、元ネタと、JSON2.js の JSON.parse と IE9 のネイティブの JSON.parse の 4 種類です。

こんな感じのコードでチェック。QUnit なのはご愛嬌ということで。

var start = new Date().getTime(),
    i = 0, length = 100000;

for(i = 0; i < length; i++){
    JSON.parseLoose("{A: { key1:val1, key2: val2  foo, key3: val3}, B: [foo, bar, true, null, 200, 0.5]}");
}
ok(true, "parseLoose = " + ((new Date().getTime() - start) / 1000) + "(seconds)");

start = new Date().getTime();
for(i = 0; i < length; i++){
    json_parse("{\"A\": { \"key1\":\"val1\", \"key2\": \"val2  foo\", \"key3\": \"val3\"}, \"B\" : [\"foo\", \"bar\", true, null, 200, 0.5]}");
}
ok(true, "json_parse = " + ((new Date().getTime() - start) / 1000) + "(seconds)");

start = new Date().getTime();
for(i = 0; i < length; i++){
    JSON2.parse("{\"A\": { \"key1\":\"val1\", \"key2\": \"val2  foo\", \"key3\": \"val3\"}, \"B\" : [\"foo\", \"bar\", true, null, 200, 0.5]}");
}
ok(true, "JSON2 = " + ((new Date().getTime() - start) / 1000) + "(seconds)");

start = new Date().getTime();
for(i = 0; i < length; i++){
    JSON.parse("{\"A\": { \"key1\":\"val1\", \"key2\": \"val2  foo\", \"key3\": \"val3\"}, \"B\" : [\"foo\", \"bar\", true, null, 200, 0.5]}");
}
ok(true, "parse = " + ((new Date().getTime() - start) / 1000) + "(seconds)");

結果は以下のような感じ。

1.parseLoose = 2.995(seconds)
2.json_parse = 1.749(seconds)
3.JSON2 = 0.94(seconds)
4.parse = 0.265(seconds)

今回作ったのが、一番遅くて IE9 の JSON.parse の 10 倍遅いですね。。

html の data-* 属性使って設定を書くときとか結構便利やと思って作ったんですが、10 倍って数字見ると悩みますね。

jQuery の Deferred でのエラー処理 その(3)

この内容は jQuery 1.7.2 を元に記述しています。

最後は $.when を見ていきます。若干残念な気分になっていたので最後こそはと期待していたのですが、結論としてはやっぱり残念な気分のままでした。

さて、$.when ですが、引数に指定した deferred を並列で全部実行した後に resolve してくれるみたいです。

$.when(
    async("Test 1", 1000),
    async("Test 2", 500),
    async("Test 3", 2000)
).done(function(){
    console.log("done - " + JSON.stringify(arguments));
});

f:id:k_maru:20120624005341p:plain

done の引数には、結果が順番に渡されるようですね。で、エラーが発生した場合ですが、非常に悲しい感じです。

$.when(
    async("Test 1", 1000),
    async("Test 2", 500, true), //ここでエラー
    async("Test 3", 2000)
).done(function(){
    console.log("done - " + JSON.stringify(arguments));
}).fail(function(){
    console.log("fail - " + JSON.stringify(arguments));
});

f:id:k_maru:20120624005553p:plain

reject された時に、すぐに fail が走ってます。。まぁ、確かにドキュメントにはそういう風に記述されているのですが。。

The method will resolve its master Deferred as soon as all the Deferreds resolve, or reject the master Deferred as soon as one of the Deferreds is rejected.

http://api.jquery.com/jQuery.when/

なにが問題かっていうと、複数エラーが発生する場合はどうするのよってところですね。あと、成功した内容も取得できないので pipe と同じように個別に then とかで成功した内容を変数にとっておかなければなりません。でも、失敗より後に成功が発生する可能性があるので、成功した内容をとっている変数の確認タイミングも工夫しなダメなんですよね。

ちょっと、どんな感じになるか書いてみようかと一瞬思いましたが、どう考えてもめんどくさい処理なのでやめときます。

ということで、 $.when での嬉しさがでるのは「一気に処理したくて完了したタイミングを知りたい。で、途中でエラーがおこってもその事実が分かればよくって、成功した内容は捨てられても、エラー後のほかの処理の継続も垂れ流されて OK」みたいな時くらいでしょうか。

pipe も $.when もそのままでは使えない感じですね。

jQuery の Deferred でのエラー処理 その(2)

この内容は jQuery 1.7.2 を元に記述しています。

jQuery の Deferred でのエラー処理 その(1)」では基本的なエラー処理をみて、「jQuery の Deferred の then について」では then がただ単純にコールバック関数を登録してるだけってのが確認できました。

ここでは、deferred を繋げて実行する pipe でのエラー処理を確認してみます。

基本的な使い方は以下のような感じ。

async("Test 1", 1000).pipe(function(e){
    return async("Test 2", 500);
}).pipe(function(e){
    return async("Test 3", 1000);
}).done(function(e){
    console.log("done - " + e);
});

結果は以下。

f:id:k_maru:20120623202013p:plain

この時点で個人的には頭を抱えてしまいそうなんですが、最後の結果だけが返ってきてるようです。先の2つの結果はどこえ消えたのか・・・。もちろん、引数が複数になっているわけではありません。仮に複数になってても第1引数になぜに最後の結果なのか、こっちも頭を抱えてしまいますが。。

調べてみると、どうやらそれぞれの結果は pipe に登録するコールバック関数の引数に渡ってくるようです。

async("Test 1", 1000).pipe(function(e){
    console.log("pipe - " + e);
    return async("Test 2", 500);
}).pipe(function(e){
    console.log("pipe - " + e);
    return async("Test 3", 1000);
}).done(function(e){
    console.log("done - " + e);
});

f:id:k_maru:20120623202025p:plain

まぁ、これはこれで分かるのですが、単純に pipe は「次の処理を行う」のではなく、「結果を渡した際に、次の処理を依頼(deferred/promiseが返却)されたら実行する」というような、動きのようです。

ここからエラーの際の動きを確かめてみますが、上記まででなんとなくどういう動きをするのかは予想がつきますね。

async("Test 1", 1000).pipe(function(e){
    /* ここで reject */
    return async("Test 2", 500, true);
}).pipe(function(e){
    return async("Test 3", 1000);
}).done(function(e){
    console.log("done - " + e);
}).fail(function(e){
    console.log("fail - " + e);
});

f:id:k_maru:20120623203117p:plain

2つ目(Test 2)で reject してるのですが、後続する pipe にエラー時のコールバックが登録されていないため、そのままなにも行われず、 fail のコールバックが実行されています。ここで、間違ってはいけないのは「エラーが起こったら、後続する pipe が飛ばされる」のではないってところですかね。ちょっと確認してみます。

/* 先頭で reject */
async("Test 1", 1000, true).pipe(function(e){
    return async("Test 2", 500);
}, function(e){
    console.log("errorback - Test2 " + e);
}).pipe(function(e){
    return async("Test 3", 1000);
}, function(e){
    console.log("errorback - Test3 " + e);
}).done(function(e){
    console.log("done - " + e);
}).fail(function(e){
    console.log("fail - " + e);
});

f:id:k_maru:20120623203947p:plain

後続する pipe にエラー時のコールバックが指定されている場合は、ちゃんと実行されていますね。ただ、さらによく見てみると2つ目以降の以降のコールバックの値が undefined になってます。これは1つ目で値を返してないからですね。ちゃんと reject で値を指定して返さないといけないようです。

/* 先頭で reject */
async("Test 1", 1000, true).pipe(function(e){
    return async("Test 2", 500);
}, function(e){
    console.log("errorback - Test2 " + e);
    return $.Deferred().reject(e);
}).pipe(function(e){
    return async("Test 3", 1000);
}, function(e){
    console.log("errorback - Test3 " + e);
    return $.Deferred().reject(e);
}).done(function(e){
    console.log("done - " + e);
}).fail(function(e){
    console.log("fail - " + e);
});

f:id:k_maru:20120623204643p:plain

もう、どれがどのコールバックなのか判別つきません。。ネストが深いのとどっちがうれしいんだか。

エラーのコールバックは登録せずに fail に任せればいいのかもしれません。が、それだと、今回の場合みたいに渡される引数の値でどの処理なのかが判別できない場合、どこでエラーが発生したのか判別できません。なので、そういう場合はやはりエラーのコールバックはそれぞれに登録する必要がありそうです。さらにそうした場合、手前で発生したエラーなのか、さらにそれ以前で発生したのかを判別する必要も出てきそうです。さらにさらに、エラーが発生した前までの結果データをもとになにか行いたい場合(補償トランザクションとか画面の再描画とか)には、結果データの保持も必要そうです。ちょっと書いてみました。

var results = [];
/* 先頭で reject */
async("", 1000, true).pipe(function(e){
    results.push(e);
    return async("", 500);
}, function(e){
    return $.Deferred().reject({ errorpos: 1, data: e });
}).pipe(function(e){
    results.push(e);
    return async("", 1000);
}, function(e){
    var result = e;
    if(!e || !e.errorpos || e.errorpos < 1){
        result = { errorpos: 2, data: e };
    }
    return $.Deferred().reject(result);
}).done(function(e){
    var i = 0, length = results.length;
    results.push(e);
    for(; i < length; i++){
        render(results[i]); //何かの処理
    }
}).fail(function(e){
    var i = 0, length = results.length;
    showerror((e.errorpos && e.data) ? e.data : e);
    for(; i < length; i++){
        revert(results[i]); //何かの処理
    }       
});

イメージコードなので、あってるかどうか確証はないですが、結構大変そうです。

ということで、pipe での嬉しさがでるのは「エラーが起こっても、単純に起こったことが取れれば OK で、どこで起こったのか、起こるまでの結果は不要」なときくらいでしょうか。もうちょっとうまいやり方(あくまでも jQuery 標準の API を使って)あってもいいのかなと思うのですが、無いんでしょうか。。。

jQuery の Deferred の then について

この内容は jQuery 1.7.2 を元に記述しています。

jQuery の Deferred でのエラー処理 その(1)」で

Deferred のインターフェイス的にはなんとなく、 then で繋いでいって、 done / fail で最後処理をするように見受けられます。

って書いてましたが、よくよく調べるとまったくそんなことは無かったです。 then はただ単純に deferred が resolve / reject / notify された時に呼び出されるコールバック関数をキューに登録するだけでした。done / fail / notify の各関数も同じでした。

ってことで、こんなコードを書いても、上から順番に関数が呼び出されるだけです。

async("Test", 1000).then(function(e){
    console.log("then 1");
}).then(function(e){
    console.log("then 2");
}).done(function(e){
    console.log("done 1");
}).then(function(e){
    console.log("then 3");
}).done(function(e){
    console.log("done 2");
});

結果は以下。

f:id:k_maru:20120623193934p:plain

deferred 単品で(pipeとかwhenとか無しに)使うことなんて無いんじゃないかって結果ですね。
ただ、さっき GitHub から開発中の jQuery のソースをとってきたら、 then に現在の pipe 相当のコードが記述されて、 pipe は then のシンタックスシュガーになってました。次のバージョンからは then で処理をつなげられるようになるのかもしれません。

jQuery の Deferred でのエラー処理 その(1)

この内容は jQuery 1.7.2 を元に記述しています。

jQuery の Deferred の使い方はそこら中にでてるのですが、詳細なエラー処理の動きについてはあまり見つからないので調べてみました。ってことで、複数回に分けて書いてみます。

とりあえず、非同期処理のファンクションとして以下のようなのを使ってます。第3引数に true を設定すると、reject でマークされるようにしてます。

var async = function(value, timeout, reject){
    var deferred = $.Deferred();
    console.log("start - " + value);
    setTimeout(function(){
        console.log("end - " + value);
        if(reject){
            deferred.reject(value);
        }else{
            deferred.resolve(value);
        }

    }, timeout);
    return deferred.promise();
};

reject された場合は、通常、 then の第2引数または fail にコールバックを設定することで取得できます。

async("test", 1000, true).then(null, function(e){
   console.log("then - " + e);
}).fail(function(e){
    console.log("fail - " + e);
});

結果は以下のような感じ。

f:id:k_maru:20120623161326p:plain

基本はこれですが、例えば、複数のデータを取得したくてサービスコールしたら、サービスの動きがイケてなくて 0 件の場合に 404 とかを返されるとかありますよね。でも、アプリケーション的には 404 のエラーではなくって 0 件のデータとして扱いたい。

ってことで、 reject されたときでも、内容によっては resolve にしたいことがあります。

Deferred のインターフェイス的にはなんとなく、 then で繋いでいって、 done / fail で最後処理をするように見受けられます。ってことで、 then の時に、 resolve でマークされた Deferred を返せば done が実行されるとうれしいです。

async("test", 1000, true).then(null, function(e){
    console.log("then - " + e);
    if(true /*本当はなにかの判定処理*/){
        return $.Deferred().resolve(e);
    }
}).done(function(e){
    console.log("done - " + e);
}).fail(function(e){
    console.log("fail - " + e);
});

これで done が実行されれば嬉しかったのですが、そうではなかったです。

f:id:k_maru:20120623163213p:plain

どうやら then では、書き換えられないようですね。なんか、 promise を繋いでいくのではなく、 promise の結果を取得して、処理を行う口になっているだけみたいです。どうもインターフェイスのイメージに合わないです。

で、これを実現するためにはどうするかを探したら pipe ってのがありました。どうやらこれが繋いでいく関数のようです。なんか、 then ってなんなのよってなりそうです。

// then じゃなくて pipe
async("test", 1000, true).pipe(null, function(e){
    console.log("then - " + e);
    if(true /*本当はなにかの判定処理*/){
        return $.Deferred().resolve(e);
    }
}).done(function(e){
    console.log("done - " + e);
}).fail(function(e){
    console.log("fail - " + e);
});

結果は期待通りです。

f:id:k_maru:20120623164304p:plain

さてさて、基本的なところはここまでですが、本来は複数の非同期処理をまとめて行いたいときに、できるだけネストをなくしてストレートに書いて結果を取りたいってのが Deferred の強みのはずなので、次回以降は pipe でのシーケンシャル実行の際のエラー処理と、 when でのパラレル実行の際のエラー処理を書きたいと思います。

WebStorm で IIS Express

最近 WebStorm を使う機会が多くなってます。
VSなら組み込みで Web サーバーを持っていて、立ち上がってくれるのですが、 WebStorm は持ってないのでちょっとしたコードを書きたいときに面倒だったりします。そこで WebStorm から IIS Express を起動できるように設定してみました。ってことで、その設定手順です。

[File] メニューから [Settings] を選択します。
f:id:k_maru:20120616120919p:plain

[Settings] ダイアログの [IDE Settings] 内にある [External Tools] を選択して、右ペインの上のほうにある [+] ボタンをクリックします。
f:id:k_maru:20120616120935p:plain

表示された [Edit Tool] ダイアログを設定します。必要なのは以下。

  • [Program]:iisexpress.exe を選択します。
  • [Parameters]:引数をしていします。引数のパスはプロジェクトのルートディレクトリが設定されるように、macro で "$FileDir$" を選択します。ポートはお好きなのをどうぞ。

f:id:k_maru:20120616120948p:plain

これで、メニューに指定した内容が表示されるようになります。
f:id:k_maru:20120616120958p:plain

もちろん、選択すると IIS Express が起動します。ちょっと文字化けしますけどね・・。
f:id:k_maru:20120616121217p:plain

これで、ちょっとしたものを書いて試したいときでも、いつでもサーバー上で動かせますね!ファイルを直接ブラウザで開いたときって、JavaScript のセキュリティ周りの挙動が違うことがあるのですが、もう安心です。

Windows 8 でのプレゼン時にタッチポイントを表示する方法

この内容は Windows 8 Consumer Preview を元に記述しています。

先日行われたWWDとかでデモとかを見ているとタッチしている指の位置が画面に表示されていて、どうやって出すのかなと思って探してみたらコンパネにありました。

コンパネにある[ペンとタッチ]を選択して、表示されたダイアログで[外部モニターでの表示に視覚的フィードバックを最適化する]を選択するだけです。

f:id:k_maru:20120503235548p:plain

以下はスタート画面で2本指でピンチしているところです。

f:id:k_maru:20120503235910p:plain