JSONPに同期リクエスト

コールバック関数を指定する形のJSONPへforループなどで複数回リクエストする場合、コールバック関数の実行される順はforループで回した順と一致するとは限らない(Opera除く)。そのため何番目にリクエストした結果に実行されたコールバック関数なのかといった処理状況をコールバック関数内で知ることが面倒だったりする(不可能というわけではない、多分)。それをあえて同期リクエストにすることでどうにかしてみようとかいう話。

同期リクエストするためには、forループなどでJSONPへのリクエストを一気に処理するのではなく、リクエストをひとつに留めてコールバック関数内で次に進むといった形で実装するが良さそう・・・とアイディアをバソキヤ求めて三千里の人に貰った。

つまり、

var urls = [
  'http://del.icio.us/feeds/json/cho45',
  'http://del.icio.us/feeds/json/drawnboy',
  'http://del.icio.us/feeds/json/gorou',
  'http://del.icio.us/feeds/json/hail2u',
  'http://del.icio.us/feeds/json/miyagawa',
  'http://del.icio.us/feeds/json/nagayama',
  'http://del.icio.us/feeds/json/typester',
  'http://del.icio.us/feeds/json/yoko'
];

というdel.icio.usで提供されているJSONフィードのURLのリストへ順番にリクエストし、その順番通りにコールバック関数を実行させるには、

addScript(urls[0] + '?callback=diplayPosts');

function addScript(url) {
  var script = document.createElement('script');
  script.setAttribute('type', 'text/javascript');
  script.setAttribute('src', url);
  script.setAttribute('charset', 'UTF-8');
  document.getElementsByTagName('head').item(0).appendChild(script);
}

function diplayPosts(posts) {
  // JSONフィードに含まれるブックマークのJSONデータに対する処理色々

  loadNext();
}

function loadNext() {
  if (urls.length <= 1) return;
  urls.shift();
  addScript(urls[0] + '?callback=diplayPosts');
}

という感じに、コールバック関数(displayPosts())内で次に進む関数(loadNext())を呼び出すように実装するということになる。こうするとコールバック関数内でurls[0]を参照すれば、どのURLへのリクエストした結果として呼び出されたコールバック関数なのかすぐにわかる。単純にforループで回した場合は、ブックマークのデータを処理するコールバック関数が呼び出される順番が狂う場合がある(ネットワーク状態に依存)し、コールバック関数内からは誰のブックマークのJSONデータを処理しているのかを知る手段がなくなってしまう。

あえて同期リクエストすることにより、非同期であることからスムースに処理できる(ことが多い)というJSONPの長所を失う上に、処理に時間がかかってしまうという欠点もある。それでもあえて非同期でリクエストしないことによって、得られるものがいくつかあるわけで、場合によってはあえて並行リクエストをしないという選択はアリなのかもしれない。

少し前に作った指定したdel.icio.usのユーザーのnetworkに登録しているユーザーのブックマークを並べるモノは誰のブックマークやらわからない状態でずらっとブックマークが並べられていたが、同期リクエストに変更することによってユーザー名付きでブックマークを並べられるようになった。