hitode909の日記

趣味はマリンスポーツですの日記です

XHRのキャッシュするやついろいろ

JavaScriptXMLHttpRequestの結果をキャッシュするの何度も書いてるけどまた書いてしまった.


去年の11月くらいに書いたのこんな感じ.responseTextをハッシュに入れるみたいな感じ.エラー出たらメッセージ出してあきらめるというのはアプリケーション固有のエラー処理だから,ここでやるのは変だと思う.

  _ajaxCache: {}

  _ajax: (url, callback) ->
    self = this
    if self._ajaxCache[url]
      callback self._ajaxCache[url]
      return

    $.ajax
      type: 'GET'
      url: url
      success: (res) ->
        self._ajaxCache[url] = res
        callback res
      error: ->
        alert('通信時にエラーが発生しました.時間をおいて試してみてください.')

    return

今年の5月にlocalStorageに保存するの書いてた.POSTする機能もついてる.jQuery Deferredを使ってるからエラーも返せる.localStorageに保存するから一度受信したらページをリロードしても一瞬で返ってくる.GETするリソースの内容が変わるときには使えない.IE7では使えない.

DataStorage =
  save: (data) ->
    dfd = $.Deferred()
    $.ajax
      url: '/data/'
      data:
        data: data
      type: 'POST'
      dataType: 'text'
      success: (key) ->
        localStorage["data-#{key}"] = data
        dfd.resolve key
      error: ->
        dfd.reject()
    dfd.promise()

  get: (key) ->
    dfd = $.Deferred()
    dataKey = "data-#{key}"

    if localStorage[dataKey]
      dfd.resolve localStorage[dataKey]
    else
      $.ajax
        url: "/data/#{key}"
        type: 'GET'
        dataType: 'text'
        success: (data) ->
          localStorage[dataKey] = data
          dfd.resolve data
        error: ->
          dfd.reject()

    dfd.promise()

  clearCache: ->
    localStorage.clear()

今年の7月に書いたやつ.仕事で書いたからコメント書いてある.リロードボタンでリロードする機能があったから,キャッシュを無効にするオプションが付いてるのと,thenでキャッシュを作ってるのがおしゃれ.

    ajaxGetOrCache : function(url, force_disable_cache) {
        // jqXHRではなくDeferredオブジェクトが返ることがあるので使う側で注意すること
        var self = this;

        // forceじゃなくてキャッシュあったら返す
        if (!force_disable_cache && self.ajaxGetOrCacheCache[url]) {
            var dfd = $.Deferred();
            dfd.resolve(self.ajaxGetOrCacheCache[url]);
            return dfd.promise();
        }

        // thenを追加してキャッシュに保存するというのをやっています
        return $.ajax({
            url: url
        }).then(function(res) {
            self.ajaxGetOrCacheCache[url] = res;
        });
    },
    ajaxGetOrCacheCache: {}

昨日書いたやつ.一箇所への通信が1つのオブジェクトになってるとよい気がしたから書いてみた.先にjQuery Deferedを返す関数を受け取ってインスタンスを作っておいて,結果が必要になったタイミングで実行する.jQuery Deferredのthenはresolveされていたら結果がすぐに返るから値をキャッシュするのに使える.

Later = function (f) {
    this.f = f;
};
Later.prototype = {
    get :  function(callback) {
        if (!this.deferred) {
            this.deferred = this.f();
        }
        return this.deferred;
    }
};

こうやって使う.

// ここではまだ通信しない
var top_page = new Later(function() {
    return $.get('/');
});

// 必要になったときにget()すると通信する
top_page.get().then(function(res) {
  console.log(res.length);
});

// 次に呼んだときはすぐに結果が返る
top_page.get().then(function(res) {
  console.log(res.length);
});

さっきのでいい気がしてたけどget()すると関数を呼んでreturnしてるだけであまり仕事してない.オブジェクト作るまでもない.
今日書いたやつ.高階関数になってる.渡された関数を最初の一回だけ呼び,それ以降そのときの結果を返す.

once = function (f) {
    var result, called;

    return function() {
        if (!called) {
            result = f();
            called = true;
        }
        return result;
    };
};

こうやって使う.

// ここではまだ通信しない
var top_page = once(function() {
    return $.get('/');
});

// ここで通信する
top_page().then(function(res) {
    console.log(res.length);
});

// ここはすぐに結果が返る
top_page().then(function(res) {
    console.log(res.length);
});


最後のシンプルだし何にでも使える.最初の一回だけalertするとか.

var hi = once(function() {
    alert('hi');
});

// 1回だけalert出る
hi();
hi();


通信結果のキャッシュしたかったのがいろいろやってるうちに汎用的なものができておもしろかった.
ここまで書いて気付いたけど最初のはURL管理してるけどあとのほうのは管理してない.アプリケーションの複数の場所から同一のURLにアクセスする場合があるときは最後のほうのもハッシュに入れたりする必要がある.シンプルになって喜んでたけど機能なくなってた.

追記