2007年 12月 05日

Deferred

jQuery Deferred とか言っていますがコアは全く jQuery に依存しないので (いい名前がおもいつかないから jQuery バインディングのほうのなまえでよんでる) http://svn.coderepos.org/share/lang/javascript/jquery-deferred/jquery-deferred.js の Deferred 関数と必要な関数をコピペしたら GM でもうごきます。setTimeout/clearTimeout にだけ依存してるのでその実装があるならどこでもうごくはず。

他の非同期なやつを Deferred 化するのは jQuery Deferred だと

function wait (n) {
	var d = new Deferred(), t = new Date();
	var id = setTimeout(function () {
		clearTimeout(id);
		d.call((new Date).getTime() - t.getTime());
	}, n * 1000)
	d.canceller   = function () { try { clearTimeout(id) } catch (e) {} };
	return d;
}

こんな感じになってます。Deferred#call がコールバック起動で、Deferred#fail がエラーバック起動です。

MochiKit のコードだと (機能がちょっと違いますが)

    /** @id MochiKit.Async.wait */
    wait: function (seconds, /* optional */value) {
        var d = new MochiKit.Async.Deferred();
        var m = MochiKit.Base;
        if (typeof(value) != 'undefined') {
            d.addCallback(function () { return value; });
        }
        var timeout = setTimeout(
            m.bind("callback", d),
            Math.floor(seconds * 1000));
        d.canceller = function () {
            try {
                clearTimeout(timeout);
            } catch (e) {
                // pass
            }
        };
        return d;
    },

引数 (value) をわたすためにチェインつかってるのがおもしろいすね。


ついでに jAutoPagerize でつかっているやつ。

function CachedResource (uri, convertfun, expire) {
	var d   = Deferred(); // new なしでいける。
	var key = uri;
	var v   = {};
	try { v = eval(GM_getValue(key)) || ({}) } catch (e) { log("parse error: may be uneval bug") }
	d.clear = function () {
		GM_setValue(key, "");
		return this;
	};
	if (v.time && v.time > (new Date).getTime() - expire) {
		log("Cache Hitted: " + key);
		setTimeout(function () { d.call(v.body); }, 10);
	} else {
		log("Cache expired; getting... " + key);
		GM_xmlhttpRequest({
			method  : "GET",
			url     : uri,
			onload  : function (req) { try {
				var res = convertfun(req.responseText);
				GM_setValue(key, uneval({time:(new Date).getTime(), body:res}));
				log(key, uneval({time:(new Date).getTime(), body:res}));
				log("Cached: " + key);
				d.call(res);
			} catch (e) { d.fail(e) } },
			onerror : function (e) {
				d.fail("HTTPError:"+e);
			}
		});
	}
	return d;
}