MochiKit で JSONP の利用
最近日本でも JSONP が広く知られるようになってきました。JSONPについて詳しくはhail2u.net - JSONP や snippets from shinichitomita’s journal - JSONPについてをどうぞ。
で、JSONP を簡単にライブラリから扱うには、dojo ならScriptSrcIO、prototype.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' });
こんな風に指定することができます。
というわけで MochiKit で JSONP を利用できるライブラリを作ってみたわけですが、不備があったらお知らせ下さい。モテキット楽しいよモテキット。
*1:#143 (Add JSONP to Async) - MochiKit - Trac はどうなったんだろ
*2:http://d.hatena.ne.jp/shinichitomita/20060522/1148276164 のソースと挙動はほとんど同じです
*3:On-Demand JavaScript と Ajax 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 で読みこまれた順から処理を…、といったことができないです。うまい解決方法とか無いのかなぁ…。