From 41eb5685b26a230327de73f51fb54558dd7ca91c Mon Sep 17 00:00:00 2001 From: Piro / SHIMODA Hiroshi Date: Sun, 27 Feb 2011 18:15:48 +0900 Subject: [PATCH] update jsdeferred --- modules/lib/jsdeferred.js | 343 +++++++++++--------------------------- 1 file changed, 93 insertions(+), 250 deletions(-) diff --git a/modules/lib/jsdeferred.js b/modules/lib/jsdeferred.js index 1f2ea8c2..a92a606b 100644 --- a/modules/lib/jsdeferred.js +++ b/modules/lib/jsdeferred.js @@ -1,101 +1,28 @@ +// Usage:: Components.utils.import('..../jsdeferred.jscodemodule.js'); +// JSDeferred 0.3.4 Copyright (c) 2007 cho45 ( www.lowreal.net ) +// See http://github.com/cho45/jsdeferred var EXPORTED_SYMBOLS = ['Deferred']; var window = {}; var location = { protocol: 'resource:' }; var document = { addEventListener : function() {} }; -Components.utils.import('resource://treestyletab-modules/lib/jstimer.jsm', window); -var setTimeout = window.setTimeout; -var clearTimeout = window.clearTimeout; -var setInterval = window.setInterval; -var clearInterval = window.clearInterval; +function setTimeout(aCallback, aDelay) { + var timer = Components.classes['@mozilla.org/timer;1'] + .createInstance(Components.interfaces.nsITimer); + timer.initWithCallback(aCallback, aDelay, timer.TYPE_ONE_SHOT); + return timer; +} + +function clearTimeout(aTimer) { + aTimer.cancel(); +} -/* Header:: - * JSDeferred - * Copyright (c) 2007 cho45 ( www.lowreal.net ) - * - * http://coderepos.org/share/wiki/JSDeferred - * - * Version:: 0.3.0 - * License:: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -/* Usage (with jQuery):: - * - * $.deferred.define(); - * - * $.get("/hoge").next(function (data) { - * alert(data); - * }). - * - * parallel([$.get("foo.html"), $.get("bar.html")]).next(function (values) { - * log($.map(values, function (v) { return v.length })); - * if (values[1].match(/nextUrl:\s*(\S+)/)) { - * return $.get(RegExp.$1).next(function (d) { - * return d; - * }); - * } - * }). - * next(function (d) { - * log(d.length); - * }); - * - */ - - -/* function Deferred () //=> constructor - * - * `Deferred` function is constructor of Deferred. - * - * Sample: - * var d = new Deferred(); - * // or this is shothand of above. - * var d = Deferred(); - */ -/* function Deferred.prototype.next (fun) //=> Deferred - * - * Create new Deferred and sets `fun` as its callback. - */ -/* function Deferred.prototype.error (fun) //=> Deferred - * - * Create new Deferred and sets `fun` as its errorback. - * - * If `fun` not throws error but returns normal value, Deferred treats - * the given error is recovery and continue callback chain. - */ -/* function Deferred.prototype.call (val) //=> this - * - * Invokes self callback chain. - */ -/* function Deferred.prototype.fail (err) //=> this - * - * Invokes self errorback chain. - */ -/* function Deferred.prototype.cancel (err) //=> this - * - * Cancels self callback chain. - */ function Deferred () { return (this instanceof Deferred) ? this.init() : new Deferred() } Deferred.ok = function (x) { return x }; Deferred.ng = function (x) { throw x }; Deferred.prototype = { + init : function () { this._next = null; this.callback = { @@ -105,11 +32,19 @@ Deferred.prototype = { return this; }, + next : function (fun) { return this._post("ok", fun) }, + + error : function (fun) { return this._post("ng", fun) }, + + call : function (val) { return this._fire("ok", val) }, + + fail : function (err) { return this._fire("ng", err) }, + cancel : function () { (this.canceller || function () {})(); return this.init(); @@ -139,11 +74,6 @@ Deferred.prototype = { } }; -/* function next (fun) //=> Deferred - * - * `next` is shorthand for creating new deferred which - * is called after current queue. - */ Deferred.next_default = function (fun) { var d = new Deferred(); var id = setTimeout(function () { d.call() }, 0); @@ -151,15 +81,14 @@ Deferred.next_default = function (fun) { if (fun) d.callback.ok = fun; return d; }; -Deferred.next_faster_way_readystatechange = ((location.protocol == "http:") && !window.opera && /\bMSIE\b/.test(navigator.userAgent)) && function (fun) { - // MSIE +Deferred.next_faster_way_readystatechange = ((typeof window === 'object') && (location.protocol == "http:") && !window.opera && /\bMSIE\b/.test(navigator.userAgent)) && function (fun) { var d = new Deferred(); var t = new Date().getTime(); if (t - arguments.callee._prev_timeout_called < 150) { var cancel = false; var script = document.createElement("script"); script.type = "text/javascript"; - script.src = "javascript:"; + script.src = "data:text/javascript,"; script.onreadystatechange = function () { if (!cancel) { d.canceller(); @@ -182,8 +111,7 @@ Deferred.next_faster_way_readystatechange = ((location.protocol == "http:") && ! if (fun) d.callback.ok = fun; return d; }; -Deferred.next_faster_way_Image = ((typeof(Image) != "undefined") && document.addEventListener) && function (fun) { - // Modern Browsers +Deferred.next_faster_way_Image = ((typeof window === 'object') && (typeof(Image) != "undefined") && !window.opera && document.addEventListener) && function (fun) { var d = new Deferred(); var img = new Image(); var handler = function () { @@ -196,24 +124,46 @@ Deferred.next_faster_way_Image = ((typeof(Image) != "undefined") && document.add img.removeEventListener("load", handler, false); img.removeEventListener("error", handler, false); }; - img.src = "data:,/ _ / X"; + img.src = "data:image/png," + Math.random(); + if (fun) d.callback.ok = fun; + return d; +}; +Deferred.next_tick = (typeof process === 'object' && typeof process.nextTick === 'function') && function (fun) { + var d = new Deferred(); + process.nextTick(function() { d.call() }); if (fun) d.callback.ok = fun; return d; }; Deferred.next = Deferred.next_faster_way_readystatechange || Deferred.next_faster_way_Image || + Deferred.next_tick || Deferred.next_default; -/* function wait (sec) //=> Deferred - * - * `wait` returns deferred that will be called after `sec` elapsed - * with real elapsed time (msec) - * - * Sample: - * wait(1).next(function (elapsed) { - * log(elapsed); //=> may be 990-1100 - * }); - */ +Deferred.chain = function () { + var chain = Deferred.next(); + for (var i = 0, len = arguments.length; i < len; i++) (function (obj) { + switch (typeof obj) { + case "function": + var name = null; + try { + name = obj.toString().match(/^\s*function\s+([^\s()]+)/)[1]; + } catch (e) { } + if (name != "error") { + chain = chain.next(obj); + } else { + chain = chain.error(obj); + } + break; + case "object": + chain = chain.next(function() { return Deferred.parallel(obj) }); + break; + default: + throw "unknown type in process chains"; + } + })(arguments[i]); + return chain; +}; + Deferred.wait = function (n) { var d = new Deferred(), t = new Date(); var id = setTimeout(function () { @@ -223,63 +173,18 @@ Deferred.wait = function (n) { return d; }; -/* function call (fun [, args...]) //=> Deferred - * - * `call` function is for calling function asynchronous. - * - * Sample: - * // like tail recursion - * next(function () { - * function pow (x, n) { - * function _pow (n, r) { - * print([n, r]); - * if (n == 0) return r; - * return call(_pow, n - 1, x * r); - * } - * return call(_pow, n, 1); - * } - * return call(pow, 2, 10); - * }). - * next(function (r) { - * print([r, "end"]); - * }); - * - */ -Deferred.call = function (f /* , args... */) { +Deferred.call = function (fun) { var args = Array.prototype.slice.call(arguments, 1); return Deferred.next(function () { - return f.apply(this, args); + return fun.apply(this, args); }); }; -/* function parallel (deferredlist) //=> Deferred - * - * `parallel` wraps up `deferredlist` to one deferred. - * This is useful when some asynchronous resources required. - * - * `deferredlist` can be Array or Object (Hash). - * - * Sample: - * parallel([ - * $.get("foo.html"), - * $.get("bar.html") - * ]).next(function (values) { - * values[0] //=> foo.html data - * values[1] //=> bar.html data - * }); - * - * parallel({ - * foo: $.get("foo.html"), - * bar: $.get("bar.html") - * }).next(function (values) { - * values.foo //=> foo.html data - * values.bar //=> bar.html data - * }); - */ Deferred.parallel = function (dl) { if (arguments.length > 1) dl = Array.prototype.slice.call(arguments); var ret = new Deferred(), values = {}, num = 0; for (var i in dl) if (dl.hasOwnProperty(i)) (function (d, i) { + if (typeof d == "function") d = Deferred.next(d); d.next(function (v) { values[i] = v; if (--num <= 0) { @@ -304,12 +209,8 @@ Deferred.parallel = function (dl) { return ret; }; -/* function earlier (deferredlist) //=> Deferred - * - * Continue process when one deferred in `deferredlist` has completed. Others will cancel. - * parallel ('and' processing) <=> earlier ('or' processing) - */ Deferred.earlier = function (dl) { + if (arguments.length > 1) dl = Array.prototype.slice.call(arguments); var ret = new Deferred(), values = {}, num = 0; for (var i in dl) if (dl.hasOwnProperty(i)) (function (d, i) { d.next(function (v) { @@ -336,24 +237,6 @@ Deferred.earlier = function (dl) { }; -/* function loop (n, fun) //=> Deferred - * - * `loop` function provides browser-non-blocking loop. - * This loop is slow but not stop browser's appearance. - * - * Sample: - * //=> loop 1 to 100 - * loop({begin:1, end:100, step:10}, function (n, o) { - * for (var i = 0; i < o.step; i++) { - * log(n+i); - * } - * }); - * - * //=> loop 10 times - * loop(10, function (n) { - * log(n); - * }); - */ Deferred.loop = function (n, fun) { var o = { begin : n.begin || 0, @@ -389,47 +272,21 @@ Deferred.loop = function (n, fun) { }; -/* function repeat (n, fun) //=> Deferred - * - * Loop `n` tiems with `fun`. - * This function automatically return control to browser, if loop time over 20msec. - * This is useful for huge loop not to block browser UI. - * - * Sample:: - * repeat(10, function (i) { - * i //=> 0,1,2,3,4,5,6,7,8,9 - * }); - */ -Deferred.repeat = function (n, f) { +Deferred.repeat = function (n, fun) { var i = 0, end = {}, ret = null; return Deferred.next(function () { var t = (new Date()).getTime(); divide: { do { if (i >= n) break divide; - ret = f(i++); + ret = fun(i++); } while ((new Date()).getTime() - t < 20); return Deferred.call(arguments.callee); } + return null; }); }; -/* function Deferred.register (name, fun) //=> void 0 - * - * Register `fun` to Deferred prototype for method chain. - * - * Sample:: - * // Deferred.register("loop", loop); - * - * // Global Deferred function - * loop(10, function (n) { - * print(n); - * }). - * // Registered Deferred.prototype.loop - * loop(10, function (n) { - * print(n); - * }); - */ Deferred.register = function (name, fun) { this.prototype[name] = function () { var a = arguments; @@ -442,33 +299,31 @@ Deferred.register = function (name, fun) { Deferred.register("loop", Deferred.loop); Deferred.register("wait", Deferred.wait); -/* Deferred.connect (func [, opts: { ok : 0, ng : null, target: null} ]) //=> Function //=> Deferred - * - * Connect a function with Deferred. That is, transform a function - * that takes a callback into one that returns a Deferred object. - * - * Sample:: - * var timeout = Deferred.connect(setTimeout, { target: window, ok: 0 }); - * timeout(1).next(function () { - * alert('after 1 sec'); - * }); - */ -// Allow to pass multiple values to next. -Deferred.Arguments = function (args) { this.args = Array.prototype.slice.call(args, 0) } -Deferred.connect = function (func, obj) { - if (!obj) obj = {}; - var callbackArgIndex = obj.ok; +Deferred.connect = function (funo, options) { + var target, func, obj; + if (typeof arguments[1] == "string") { + target = arguments[0]; + func = target[arguments[1]]; + obj = arguments[2] || {}; + } else { + func = arguments[0]; + obj = arguments[1] || {}; + target = obj.target; + } + + var partialArgs = obj.args ? Array.prototype.slice.call(obj.args, 0) : []; + var callbackArgIndex = isFinite(obj.ok) ? obj.ok : obj.args ? obj.args.length : undefined; var errorbackArgIndex = obj.ng; - var target = obj.target; return function () { - var d = new Deferred(); + var d = new Deferred().next(function (args) { + var next = this._next.callback.ok; + this._next.callback.ok = function () { + return next.apply(this, args.args); + }; + }); - d.next = function (fun) { return this._post("ok", function () { - fun.apply(this, (arguments[0] instanceof Deferred.Arguments) ? arguments[0].args : arguments); - }) }; - - var args = Array.prototype.slice.call(arguments, 0); + var args = partialArgs.concat(Array.prototype.slice.call(arguments, 0)); if (!(isFinite(callbackArgIndex) && callbackArgIndex !== null)) { callbackArgIndex = args.length; } @@ -481,24 +336,10 @@ Deferred.connect = function (func, obj) { Deferred.next(function () { func.apply(target, args) }); return d; } -} +}; +Deferred.Arguments = function (args) { this.args = Array.prototype.slice.call(args, 0) }; -/* Deferred.retry(retryCount, func [, options = { wait : 0 } ]) - * - * Try func (returns Deferred) max `retryCount`. - * - * Sample:: - * Deferred.retry(3, function () { - * return http.get(...); - * }). - * next(function (res) { - * res //=> response if succeeded - * }). - * error(function (e) { - * e //=> error if all try failed - * }); - */ -Deferred.retry = function (retryCount, funcDeferred/* funcDeferred() return Deferred */, options) { +Deferred.retry = function (retryCount, funcDeferred, options) { if (!options) options = {}; var wait = options.wait || 0; @@ -519,10 +360,11 @@ Deferred.retry = function (retryCount, funcDeferred/* funcDeferred() return Defe }; setTimeout(retry, 0); return d; -} +}; +Deferred.methods = ["parallel", "wait", "next", "call", "loop", "repeat", "chain"]; Deferred.define = function (obj, list) { - if (!list) list = ["parallel", "wait", "next", "call", "loop", "repeat"]; + if (!list) list = Deferred.methods; if (!obj) obj = (function getGlobal () { return this })(); for (var i = 0; i < list.length; i++) { var n = list[i]; @@ -531,3 +373,4 @@ Deferred.define = function (obj, list) { return Deferred; }; +this.Deferred = Deferred;// End of JSDeferred