ボーイフレンドを直す方法 あるいは賢いjQuery.Deferredの使い方

f:id:mizchi:20131217104008j:plain

問題

モバイルは回線が不安定なので、ロードの失敗が頻繁に起こります。 開発時は高速なwifi環境で開発しているので、リリース間近になって帯域を圧迫していることに気づいたりします。

解決方法

画像を先読みします

var preload = function(src){
  var d = $.Deferred(); 
  var img = new Image;
  img.src= src;
  img.onload = d.resolve
  img.onerror = d.reject
  return d.promise();
};

何をやっているかというと、空のimgタグをつくってそこに画像を読み込みます。その過程でブラウザキャッシュに画像が保存されます。正確に言うとこの時点ではどこにも紐付いていないのでGC対象ですが、その後すぐDOMに画像をはるなら問題ありません。

並列で先読みする(速い・不安定)

var preload_images_paralell = function(srcs){
  var dfds = srcs.map(function(src){return preload(src);});
  return $.when.apply($, dfds);
}
preload_images_paralell(["boy.png", "boy2.png"]).done(function(){
  console.log('loading done');
});

一応先読みしておきたいけど、そこまで安定性が高いわけじゃないです。画面に貼った時点でDOM要素のwidth, heightが確定しているので、画面がガコガコと再配置されて負荷がかかるのを防ぐのが目的です。

直列で先読みする(遅い・安定)

var preload_images_serial = function(srcs){
  var d = $.Deferred();
  var src = srcs.shift();
  if(src) preload(src).done(function(){preload_images_serial(srcs)};)
  else d.resolve()
  return d.promise();
};
preload_images_serial(["boy.png", "boy2.png"]).done(function(){
  console.log('loading done');
});

一個一個読み込むので遅いです。その分安定します。UIパーツを構成する必須要素などで使います。 まあ、それ以前に、そもそも裏でいくつのHTTPリクエスト飛んでるか意識したほうがいいです。

まだ残ってる問題

  • 平気でImage#onloadもImage#onerrorも通らずに諦めるリクエストがある
  • 壊れた画像(しかしブラウザは正常だと認識している)を破棄する方法がない
  • IEだとかは何も言わずに諦めるケースがある
  • サーバー側で諦めてる場合はどうしようもない

:追記