前回に引き続き、Wikipediaから緯度経度を取得しGoogle Maps上にプロットするアプリケーションを作っていきます。
今回は透過的なキャッシュの仕組みを入れるのと、
完成コードは以下のようになります。
透過的なキャッシュ
キャッシュのための領域として、
localStorageは同期的に値を取得やセットをできますが、
前回は、
$.getJSON(url).
next(function (data) {
// ...
});
はじめに、
withCache('wikipediajson', function () { return $.getJSON(url) }).
next(function (data) {
// ...
});
withCache(key,fun);はkeyをキャッシュキーにしてfunが返すデータをキャッシュする関数で、
withCache関数を作るために、
function foobar () {
var d = new Deferred;
setTimeout(function () { d.call('done!') }, 0);
return d;
}
foobar().next(function (r) {
alert(r); //=> 'done!'
});
これでDeferredオブジェクトを返す関数を作ることができます。
withCacheでは、
function withCache (key, fun) {
var d = new Deferred();
var val = localStorage.getItem(key);
if (val) {
next(function () {
d.call(eval(val));
});
} else {
fun().next(function (data) {
try {
localStorage.setItem(key, uneval(data));
} catch (e) {
localStorage.clear(); // キャッシュが溢れた
localStorage.setItem(key, uneval(data));
}
d.call(data);
});
}
return d;
}
localStorageにデータがあるときはnext()関数で呼び出しを非同期化した上でcall()メソッドを呼んでいます。JavaScriptで非同期処理を同期化することは決してできないのは先に言及したとおりですが、
この同期処理をnext()で非同期にするということをせずに、
localStorageにデータがないときは、
geolocationによる現在位置表示
次に、
まずは、
function getCurrentLocation (callback) {
var geo = navigator.geolocation || google.gears.factory.create('beta.geolocation');
var id = geo.watchPosition(
function (pos) {
geo.clearWatch(id);
callback(pos);
},
function (e) {
},
{
enableHighAccuracy: true,
maximumAge: 0,
gearsLocationProviderUrls: []
}
);
}
ここではまだJSDeferredを使用しません。なぜなら、
この関数はgearsによるgeolocationの実装と、
さて、
Deferred.
var getLocation = Deferred.connect(getCurrentLocation);
getLocation().next(function (loc) {
});
簡単ですね。これで他のJSDeferredの関数にようにnext()で繋いでいくことができます。では、
geolocationの情報取得には多少時間がかかるため、
parallelは複数の非同期処理を実行し、
parallel({
wikipedia: CollectLatLongFromCategory(...) // 前回書いた関数
mylocation: getLocation()
}).
next(function (result) {
// どちらの実行も終わったあとに呼ばれる
alert(result.mylocation.coords.latitude, result.mylocation.coords.longitude);
});
CollectLatLongFromCategoryは前回と今回で取り上げた、
getLocationは今Deferred.
parallelは両方の結果が揃ってから、
parallel({
wikipedia: CollectLatLongFromCategory(...) // 前回書いた関数
mylocation: getLocation()
}).
next(function (result) {
var lat = result.mylocation.coords.latitude;
var lng = result.mylocation.coords.longitude;
var myPos = new google.maps.LatLng(lat, lng);
var marker = new google.maps.Marker({
position : myPos,
map : map,
icon : markerImageMe,
title : 'あなた'
});
var nearest, nearestPos;
for (var i = 0, len = markers.length; i < len; i++) {
var pos = markers[i].getPosition();
var dis = distance(pos, myPos);
if (!nearestPos || dis < nearestPos) {
nearestPos = dis;
nearest = pos;
}
}
var bounds = new google.maps.LatLngBounds();
bounds.extend(myPos);
bounds.extend(nearest);
map.fitBounds(bounds);
map.setZoom(map.getZoom() - 1);
});
マーカーの初期化などは省略しましたが、
まとめ
前回と今回でJSDeferredを使った簡単なJavaScriptアプリケーションを作ってみました。もしよろしければ、
煩雑な非同期処理が多くなりがちなJavaScriptでは、
次回はJSDeferredによるパフォーマンスの最適化と、