MochiKit で JSONP の利用

最近日本でも JSONP が広く知られるようになってきました。JSONPについて詳しくはhail2u.net - JSONPsnippets from shinichitomita’s journal - JSONPについてをどうぞ。
で、JSONP を簡単にライブラリから扱うには、dojo ならScriptSrcIOprototype.js なら dojoの ScriptSrcIO ライクに拡張した Dojo penetrates Prototype Dude, WTF?! などがあります。
しかしながら、我らが MochiKit は開発者の Bob さんが JSONP を提唱したにもかかわらず、JSONP を扱う Async の関数が見あたらない*1ので作ってみました。デモでは delicious の json api をコールバック関数付きで呼び出し(JSONP)て、リストを取得し表示しています。

http://rails2u.com/misc/MochiKitAsyncJSONP/ *2
http://rails2u.com/misc/MochiKitAsyncJSONP/AsyncJSONP.js (ソース)

MochiKit では非同期の xmlhttprequest や setTimeout は Deferred インスタンスを介して扱います。create script element による JSONP で使われるコールバック関数呼び出しのテクニック*3も非同期なため Deffered オブジェクトを返すようにしています。このライブラリを利用した実装のコードはこんな感じ。

var d = sendJSONPRequest('http://del.icio.us/feeds/json/' + $('uid').value, 'callback');
d.addCallback(function(json) {
    replaceChildNodes($('result'), UL(null, map(function(data) {
        return UL(null, A({href: data.u}, data.d));
    }, json)));
});

sendJSONPRequest で JSON の url とクエリーのコールバック名(delicious では ?callback=関数名 なので 'callback' となる) を引数に渡し Deffered インスタンスを取得します。で、その Deffered インスタンスにコールバック関数を渡していく、という普通の MochiKit の Async な方法で利用します。
また、sendJSONPRequest の第三引数にオプションとして charset など、script 要素の属性を指定することも可能です。

var d = sendJSONPRequest(url, 'callback', {
  charset: 'utf-8'
});

こんな風に指定することができます。
というわけで MochiKitJSONP を利用できるライブラリを作ってみたわけですが、不備があったらお知らせ下さい。モテキット楽しいよモテキット

*1:#143 (Add JSONP to Async) - MochiKit - Trac はどうなったんだろ

*2:http://d.hatena.ne.jp/shinichitomita/20060522/1148276164 のソースと挙動はほとんど同じです

*3:On-Demand JavaScriptAjax Patterns では呼ばれてるみたいです。by kyoさん

JSONP が Opera だと非同期処理できない

先日のエントリー、MochiKit で JSONP の利用の AysncJSONP.js を拡張して、リクエスト先が重くて開けなかったり、シンタックスエラーで読み込みに失敗した時用に、タイムアウトのエラー処理を追加しようとしたときに気づきました。
Opera では element.appendChild(script) による、スクリプト要素の動的追加では、追加完了までその場で実行処理が止まっています。そのため、

document.getElementsByTagName('head')[0].appendChild(script);
alert('example');

という処理の場合、追加するスクリプト要素のソース先のサーバが重くて、結果を返すのに10秒かかかったら alert が実行されるのが10秒後となってしまいます。WinIE や FireFox の場合は、スクリプト要素は非同期で追加されるため、スムーズに処理ができるのですが、Opera だとそれができません。
そのため Opera では非同期で JSONP で読みこまれた順から処理を…、といったことができないです。うまい解決方法とか無いのかなぁ…。