From 0a6e61ee36beb445d05d2aaf1bb57c4c498d961c Mon Sep 17 00:00:00 2001 From: minodisk Date: Thu, 7 Mar 2013 13:35:14 +0900 Subject: [PATCH] Add JSDeferred --- README.md | 1 + jsdeferred/jsdeferred-tests.ts | 858 +++++++++++++++++++++++++++++++++ jsdeferred/jsdeferred.d.ts | 98 ++++ 3 files changed, 957 insertions(+) create mode 100644 jsdeferred/jsdeferred-tests.ts create mode 100644 jsdeferred/jsdeferred.d.ts diff --git a/README.md b/README.md index 1377dce531..8ba366b935 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,7 @@ List of Definitions * [jQuery.Transit](http://ricostacruz.com/jquery.transit/) (by [MrBigDog2U](https://github.com/MrBigDog2U)) * [jQuery.Validation](http://bassistance.de/jquery-plugins/jquery-plugin-validation/) (by [Boris Yankov](https://github.com/borisyankov)) * [jQuery.Watermark](http://jquery-watermark.googlecode.com) (by [Anwar Javed](https://github.com/anwarjaved)) +* [JSDeferred](http://cho45.stfuawsc.com/jsdeferred/) (by [Daisuke Mino](https://github.com/minodisk)) * [JSONEditorOnline](https://github.com/josdejong/jsoneditoronline) (by [Vincent Bortone](https://github.com/vbortone/)) * [KeyboardJS](https://github.com/RobertWHurst/KeyboardJS) (by [Vincent Bortone](https://github.com/vbortone/)) * [Knockback](http://kmalakoff.github.com/knockback/) (by [Marcel Binot](https://github.com/docgit)) diff --git a/jsdeferred/jsdeferred-tests.ts b/jsdeferred/jsdeferred-tests.ts new file mode 100644 index 0000000000..a42119bce2 --- /dev/null +++ b/jsdeferred/jsdeferred-tests.ts @@ -0,0 +1,858 @@ +/// +/// + +declare function alert(a:any):void; +declare function log(a:any):void; + +interface DeferredizedJQueryStatic extends JQueryStatic { + get(url:string, data?:any, success?:any, dataType?:any): Deferred; +} +declare var $: DeferredizedJQueryStatic; + +declare interface http { + get(url:string):Deferred; +} + + +/***** jsDoc case *****/ + + // constructor +(()=> { + var d = new Deferred(); + var d = Deferred(); +})(); + +// define +(()=> { + 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); + }); +})(); + +// next +(()=> { + var d = new Deferred(); + d. + next(function () { + alert(1); + }). + next(function () { + alert(2); + }); + d.call(); +})(); + +// error +(()=> { + var d = new Deferred(); + d. + next(function () { + alert(1); + throw "foo"; + }). + next(function () { + alert('not shown'); + }). + error(function (e) { + alert(e); //=> "foo" + }); + d.call(); +})(); + +// call +(()=> { + var d = new Deferred(); + setTimeout(function () { + d.call('value'); + }, 100); + return d; +})(); + +// fail +(()=> { + var d = new Deferred(); + var x = new XMLHttpRequest(); + x.onreadystatechange = function () { + if (x.readyState == 4) { + if (x.status == 200) d.call(x); else d.fail(x); + } + }; + return d; +})(); + +// chain +(()=> { + chain( + function () { + return wait(0.5); + }, + function (w) { + throw "foo"; + }, + function error(e) { + alert(e); + }, + [ + function () { + return wait(1); + }, + function () { + return wait(2); + } + ], + function (result) { + alert([ result[0], result[1] ]); + }, + { + foo: wait(1), + bar: wait(1) + }, + function (result) { + alert([ result.foo, result.bar ]); + }, + function error(e) { + alert(e); + } + ); +})(); + +// wait +(()=> { + wait(1).next(function (elapsed) { + log(elapsed); //=> may be 990-1100 + }); +})(); + +// call +(()=> { + 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"]); + }); +})(); + +// parallel +(()=> { + 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 + }); +})(); + +// loop +(()=> { + //=> 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 with sleeping 1 sec in each loop. + loop(10, function (n) { + log(n); + return wait(1); + }); +})(); + +// repeat +(()=> { + repeat(10, function (i) { + i //=> 0,1,2,3,4,5,6,7,8,9 + }); +})(); + +// register +(()=> { + // Deferred.register("loop", loop); + + // Global Deferred function + loop(10,function (n) { + print(n); + }). + // Registered Deferred.prototype.loop + loop(10, function (n) { + print(n); + }); +})(); + +// connect +(()=> { + var timeout = Deferred.connect(setTimeout, { target: window, ok: 0 }); + timeout(1).next(function () { + alert('after 1 sec'); + }); + + var timeout = Deferred.connect(window, "setTimeout"); + timeout(1).next(function () { + alert('after 1 sec'); + }); +})(); + +// retry +(()=> { + Deferred.retry(3,function () { + return http.get('foo.html'); + }). + next(function (res) { + res //=> response if succeeded + }). + error(function (e) { + e //=> error if all try failed + }); +})(); + + +/***** Tutorial case *****/ + +(()=> { + Deferred.define(); + + next(function () { + alert("Hello!"); + return wait(5); + }). + next(function () { + alert("World!"); + }); + + Deferred.next(function () { + alert("Hello!"); + return Deferred.wait(5); + }). + next(function () { + alert("World!"); + }); + + // http.get is assumed to be a function that takes a URI as an argument and returns a Deferred instance + var results = []; + next(function () { + return http.get("/foo.json").next(function (data) { + results.push(data); + }); + }). + next(function () { + return http.get("/baz.json").next(function (data) { + results.push(data); + }); + }). + next(function () { + return http.get("/baz.json").next(function (data) { + results.push(data); + }); + }). + next(function () { + alert(results); + }); + + var wants = ["/foo.json", "/bar.json", "/baz.json"]; + var results = []; + loop(wants.length,function (i) { + return http.get(wants[i]).next(function (data) { + results.push(data); + }); + }). + next(function () { + alert(results); + }); + + parallel([ + http.get("/foo.json"), + http.get("/bar.json"), + http.get("/baz.json") + ]). + next(function (results) { + alert(results); + }); + + next(function () { + // something 1 + }). + next(function () { + // asynchronous process + throw "error!"; + }). + next(function () { + // something 2 (not executed as an error occurs in the previous process) + }); + + next(function () { + // something 1 + }). + next(function () { + // asynchronous process + throw "error!"; + }). + next(function () { + // something 2 (not executed as an error occurs in the previous process) + }). + error(function (e) { + alert(e); + }); + + next(function () { + // something 1 + }). + next(function () { + // asynchronous process + throw "error!"; + }). + error(function (e) { + alert(e); + }). + next(function () { + // something 2 (executed since the exception would already be handled) + }). + error(function (e) { + alert(e); + }); + + next(function () { + alert("Hello!"); + return wait(5); + }). + next(function () { + alert("World!"); + }); + + next(function () { + alert(1); + return next(function () { + alert(2); + }). + next(function () { + alert(3); + }); + }). + next(function () { + alert(4); + }); + + var http = {} + http.get = function (uri) { + var deferred = new Deferred(); + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function () { + if (xhr.readyState == 4) { + if (xhr.status == 200) { + deferred.call(xhr); + } else { + deferred.fail(xhr); + } + } + }; + deferred.canceller = function () { xhr.abort() }; + return deferred; + } + + loop(1000, function (n) { + // heavy process + }); + + function repeat(n, f) { + 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++); + } while ((new Date()).getTime() - t < 20); + return Deferred.call(arguments.callee); + } + }); + } + + next(function () { + console.log("start"); + }). + next(function () { + function pow(x, n) { + function _pow(n, r) { + console.log([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) { + console.log([r, "end"]); + }). + error(function (e) { + alert(e); + }) + + loop(10, function (i) { + console.log(i) + }); + + $.get("README.markdown").next(function (data) { + console.log(data); + }); + +})(); + +(()=> { + console.log("start. gathering data."); + + parallel([$.get("README.markdown"), $.get("ChangeLog")]). + next(function (values) { + var lengths = $.map(values, function (i) { return i.length }); + console.log(lengths.join(", ")); + }); +})(); + +(()=> { + console.log("start. gathering data."); + + parallel({html: $.get("README.markdown"), js: $.get("ChangeLog")}). + next(function (values) { + console.log(["html=", values.html.length, " js=", values.js.length].join("")); + }); +})(); + +(()=> { + console.log("start. wait 3 sec."); + + var list = []; + var printAndReturn = function (i) { + console.log(i + "msec elapsed"); + return i; + }; + list.push(wait(0).next(printAndReturn)); + list.push(wait(1).next(printAndReturn)); + list.push(wait(2).next(printAndReturn)); + list.push(wait(3).next(printAndReturn)); + + parallel(list).next(function (values) { + console.log("Completed. values: " + values.join(", ")); + }); +})(); + +(()=> { + var queue = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + var workers = new Array(2); + var work = function (job) { + console.log('working... ' + job); + return wait(Math.random() * 4); + }; + + for (var i = 0, len = workers.length; i < len; i++) { + workers[i] = next(function me() { + var job = queue.shift(); + if (!job) return; + + console.log("start worker: " + job); + return next(function () { return job }). + next(work). + next(me); + }). + error(function (e) { + alert(e); + }); + } + + parallel(workers).next(function () { + console.log('all done!'); + }); +})(); + +(()=> { + next(function () { + var sum = 0; + return loop({end: 100000, step: 1000}, function (n, o) { + console.log(["Processing divided loop:n=", n, ", sum=", sum, " last?=", o.last].join("")); + for (var i = 0; i < o.step; i++) { + // console.log(i + n); + sum += i + n; + } + console.log(["sum=", sum].join("")); + return sum; + }); + }). + next(function (e) { + console.log("Result:" + e); + console.log("end"); + }). + error(function (e) { + console.log(e); + }); +})(); + +(()=> { + loop({begin: 1, end: 100, step: 10}, function (n, o) { + console.log(["Processing divided loop:n=", n, " last?=", o.last].join("")); + for (var i = 0; i < o.step; i++) { + var j = n + i; + console.log(j); + } + }); +})(); + +(()=> { + Deferred.repeat(100, function (n, o) { + console.log(n); + for (var i = 0; i < Math.pow(n, 2); i++) { + for (var j = n; j; j--); + } + }); +})(); + +(()=> { + loop(10, function (n) { + console.log(n); + return wait(0.1); + }); + + loop(10, function (n) { + console.log(String.fromCharCode(97 + n)); + return wait(0.2); + }); +})(); + +(()=> { + console.log(0); + loop(10,function (n) { + console.log(n); + return n; + }). + wait(1). + loop(10,function (n) { + var c = String.fromCharCode(97 + n); + console.log(c); + return c; + }). + next(function (i) { + console.log("end"); + }). + error(function (e) { + alert(e); + }); +})(); + +(()=> { + loop(5,function (i, o) { + console.log(i); + return o.last ? i : wait(1); + }). + next(function (e) { + console.log("end [" + e + "]"); + }). + error(function (e) { + console.log(e); + }); +})(); + +(()=> { + next(function (i) { + function delayloop(i) { + console.log(i++); + if (i < 5) { + return wait(1).next(function () { + return call(delayloop, i); + }); + } + } + + return call(delayloop, 0); + }). + next(function (e) { + console.log("end"); + }). + error(function (e) { + console.log(e); + }); +})(); + +(()=> { + function callcc(fun) { + var error = new Deferred(); + return call(function () { + // JSDeferred passes current Deferred Object to this. + var ccdeferred = this; + // Call with current continuation (calling Deferred.next) + return fun(function (a) { + ccdeferred._next.call(a); + throw error + }); + }). + error(function (e) { + // Current Deferred chain must be stopped + if (e === error) { + return e; + } else { + throw e; + } + }); + } + + callcc(function (cont) { + return 10 * 10 * cont(20); + }). + next(function (val) { + console.log("callcc1 returns:" + val); + }); + // should show "callcc1 returns:20" + + var cont; + var i = 0; + callcc(function (c) { + cont = c; + return 10; + }). + next(function (val) { + console.log("callcc2 returns:" + val); + if (!i++) cont(20); + }); + // should show "callcc2 returns:10", "callcc returns:20" +})(); + +(()=> { + function callcc(fun) { + var error = new Deferred(); + return call(function () { + // JSDeferred passes current Deferred Object to this. + var ccdeferred = this; + // Call with current continuation (calling Deferred.next) + return fun(function (a) { + ccdeferred._next.call(a); + throw error + }); + }). + error(function (e) { + // Current Deferred chain must be stopped + if (e === error) { + return e; + } else { + throw e; + } + }); + } + + // http://www.sampou.org/scheme/t-y-scheme/t-y-scheme-Z-H-16.html#node_chap_14 + // Just port above. + function amb() { + var alts = arguments; + var prevAmbFail = amb.ambFail; + + return callcc(function (sk) { + return loop(alts.length,function (i) { + var alt = alts[i]; + return callcc(function (fk) { + amb.ambFail = function () { + amb.ambFail = prevAmbFail; + return fk("fail"); + }; + return sk(alt); + }); + }). + next(prevAmbFail); + }); + } + + amb.ambFail = function () { throw "amb tree exhausted" }; + + // Utility function + function amb1(ambvars) { + var f = wait(0); + var vars = {}; + for (var k in ambvars) if (ambvars.hasOwnProperty(k)) (function (name, val) { + console.log(name); + f = f.next(function () { + return amb.apply(this, val).next(function (i) { + vars[name] = i; + return vars; + }); + }); + })(k, ambvars[k]); + + return f; + } + + function assert(cond) { + if (!cond) throw amb(); + } + + // http://mayokara.info/note/view/251 + Array.prototype.uniq = function () { + for (var i = 0, l = this.length; i < l; i++) { + if (this.indexOf(this[i]) < i) { + this.splice(i--, l-- && 1); + } + } + return this; + }; + + amb1({ + baker : [1, 2, 3, 4, 5], + cooper : [1, 2, 3, 4, 5], + fletcher: [1, 2, 3, 4, 5], + miller : [1, 2, 3, 4, 5], + smith : [1, 2, 3, 4, 5] + }). + next(function (vars) { + with (vars) { + console.log(vars); + // 簡易 distinct + assert([baker, cooper, fletcher, miller, smith].uniq().length == 5); + console.log("distinct passed"); + assert(baker != 5); + assert(cooper != 1); + assert(fletcher != 1 && fletcher != 5); + assert(miller > cooper); + assert(Math.abs(smith - fletcher) != 1); + assert(Math.abs(fletcher - cooper) != 1); + + return vars; + } + }). + next(function (vars) { + with (vars) { + console.log("solved"); + console.log(vars); + alert(uneval(vars)); + } + }). + error(function (e) { + alert(e) + }); +})(); + +(()=> { + var d1 = new Deferred(); + d1.callback.ok = function () { + alert("1"); + }; + + var d2 = new Deferred(); + d2.callback.ok = function () { + alert("2"); + }; + + // Set d2 as continuation of d1. + d1._next = d2; + + // Invoke the chain. + d1.call(); +})(); + +(()=> { + next(function () { // this `next` is global function + alert("1"); + }). + next(function () { // this `next` is Deferred#next + alert("2"); + }). + next(function () { + alert("3"); + }); +})(); + +(()=> { + next(function () { + alert("1"); + }). + next(function () { + alert("2"); + // child Deferred + return next(function () { + alert("3"); + }); + }). + next(function () { + alert("4"); + }); +})(); + +(()=> { + next(function () { + alert("1"); + }). + next(function () { + alert("2"); + var d = next(function () { + alert("3"); + }); + d._next = this._next; + this.cancel(); + }). + next(function () { + alert("4"); + }); +})(); + +(()=> { + next(function () { + alert("1"); + }). + next(function () { + alert("2"); + next(function () { + alert("3"); + }). + next(function () { + alert("4"); + }); + }); +})(); + +(()=> { + next(function () { + throw "Error"; + }). + error(function (e) { + expect("Errorback called", "Error", e); + return e; // recovering error + }). + next(function (e) { + expect("Callback called", "Error", e); + throw "Error2"; + }). + next(function (e) { + // This process is not called because + // the error is not recovered. + ng("Must not be called!!"); + }). + error(function (e) { + expect("Errorback called", "Error2", e); + }); +})(); diff --git a/jsdeferred/jsdeferred.d.ts b/jsdeferred/jsdeferred.d.ts new file mode 100644 index 0000000000..1e354e7ee5 --- /dev/null +++ b/jsdeferred/jsdeferred.d.ts @@ -0,0 +1,98 @@ +// Type definitions for JSDeferred +// Project: https://github.com/cho45/jsdeferred +// Definitions by: Daisuke Mino +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +interface Loop { + begin:number; + end:number; + step:number; +} + +interface ConnectOption { + target:any; + args?:any[]; + ok?:number; + ng?:number; +} + +interface RetryOption { + wait:number; +} + +interface DeferredizedFunction { (...arg:any[]):Deferred; } +interface DeferredizedFunctionWithNumber { (n:number):Deferred; } +interface FunctionWithNumber { (i:number); } +interface ErrorCallback { (d:Deferred, ...args:any[]); } + +declare class Deferred { + + static methods:string[]; + + static isDeferred(obj:any):bool; + + static next(fun:Function):Deferred; + + static chain(...args:any[]):Deferred; + + static wait(n:number):Deferred; + + static call(fun?:Function, ...args:any[]):Deferred; + + static parallel(dl:any):Deferred; + + static earlier(dl:any):Deferred; + + static loop(n:number, fun:FunctionWithNumber):Deferred; + + static loop(n:Loop, fun:FunctionWithNumber):Deferred; + + static repeat(n:number, fun:FunctionWithNumber):Deferred; + + static register(name:string, fun:DeferredizedFunction):void; + + static connect(funo:any, options:string):DeferredizedFunction; + + static connect(funo:Function, options?:ConnectOption):DeferredizedFunction; + + static retry(retryCount:number, funcDeferred:DeferredizedFunctionWithNumber, options?:RetryOption):Deferred; + + static define(obj?:any, list?:string[]):any; + + constructor(); + + next(fun:Function):Deferred; + + wait(n:number):Deferred; + + error(fun:ErrorCallback):Deferred; + + call(val?:any):Deferred; + + fail(err:any):Deferred; + + cancel():Deferred; + + parallel(dl:any):Deferred; + + loop(n:number, fun:FunctionWithNumber):Deferred; + + loop(n:Loop, fun:FunctionWithNumber):Deferred; + +} + + +declare function chain(...args:any[]):Deferred; + +declare function wait(n:number):Deferred; + +declare function call(fun?:Function, ...args:any[]):Deferred; + +declare function parallel(dl:any):Deferred; + +declare function earlier(dl:any):Deferred; + +declare function loop(n:number, fun:FunctionWithNumber):Deferred; +declare function loop(n:Loop, fun:FunctionWithNumber):Deferred; + +declare function repeat(n:number, fun:FunctionWithNumber):Deferred; \ No newline at end of file