Skip to content

Commit

Permalink
dojox.lang new module: async (a helper for event-driven programming),…
Browse files Browse the repository at this point in the history
… !strict, refs #10625.

git-svn-id: http://svn.dojotoolkit.org/src/dojox/trunk@21140 560b804f-0ae3-0310-86f3-f6aa0a117693
  • Loading branch information
uhop committed Jan 12, 2010
1 parent 76123c9 commit f3e1fcd
Show file tree
Hide file tree
Showing 7 changed files with 434 additions and 3 deletions.
15 changes: 13 additions & 2 deletions lang/README
Expand Up @@ -13,8 +13,15 @@ Credits
-------------------------------------------------------------------------------
Project description

Implementation of common functional operations, and provisions, aspect-oriented
helpers. Later we can add other JS language-related helpers.
dojox.lang.functional - Provides lambda functions, and common functional
operations.

dojox.lang.aspect - Provides a framework for aspect-oriented programming.

dojox.lang.oo - Provides mixers to support traits and mixins for object-oriented
programming.

dojox.lang.async - Provides helpers for event-driven programming.

dojox.lang.observable - Provides construction of objects that such that
property access and modification can be controlled, i.e. provides a form of
Expand All @@ -40,6 +47,10 @@ For now:

dojox.lang.functional:
http://lazutkin.com/blog/2008/jan/12/functional-fun-javascript-dojo/
http://lazutkin.com/blog/2008/jun/30/using-recursion-combinators-javascript/

dojox.lang.aspect:
http://lazutkin.com/blog/2008/may/18/aop-aspect-javascript-dojo/

-------------------------------------------------------------------------------
Installation instructions
Expand Down
200 changes: 200 additions & 0 deletions lang/async.js
@@ -0,0 +1,200 @@
dojo.provide("dojox.lang.async");

(function(){
var d = dojo, Deferred = d.Deferred, each = d.forEach, some = d.some,
async = dojox.lang.async, aps = Array.prototype.slice,
opts = Object.prototype.toString;

async.seq = function(x){
// summary:
// Executes functions sequentially. Waits if any of them returns Deferred.
var fs = opts.call(x) == "[object Array]" ? x : arguments;
return function(init){
var x = new Deferred();
each(fs, function(f){ x.addCallback(f); });
x.callback(init);
return x;
};
};

async.par = function(x){
// summary:
// Executes functions in parallel. Waits for all of them to finish.
var fs = opts.call(x) == "[object Array]" ? x : arguments;
return function(init){
var results = new Array(fs.length),
cancel = function(){
each(results, function(v){
if(v instanceof Deferred && v.fired < 0){
v.cancel();
}
});
},
x = new Deferred(cancel),
ready = fs.length;
each(fs, function(f, i){
var x;
try {
x = f(init);
}catch(e){
x = e;
}
results[i] = x;
});
var failed = some(results, function(v){
if(v instanceof Error){
cancel();
x.errback(v);
return true;
}
return false;
});
if(!failed){
each(results, function(v, i){
if(v instanceof Deferred){
v.addCallbacks(
function(v){
results[i] = v;
if(!--ready){
x.callback(results);
}
},
function(v){
cancel();
x.errback(v);
}
);
}else{
--ready;
}
});
}
if(!ready){
x.callback(results);
}
return x;
};
};

async.any = function(x){
// summary:
// Executes functions in parallel. As soon as one of them finishes
// cancels the rest.
var fs = opts.call(x) == "[object Array]" ? x : arguments;
return function(init){
var results = new Array(fs.length), noResult = true;
cancel = function(index){
each(results, function(v, i){
if(i != index && v instanceof Deferred && v.fired < 0){
v.cancel();
}
});
},
x = new Deferred(cancel);
each(fs, function(f, i){
var x;
try {
x = f(init);
}catch(e){
x = e;
}
results[i] = x;
});
var done = some(results, function(v, i){
if(!(v instanceof Deferred)){
cancel(i);
x.callback(v);
return true;
}
return false;
});
if(!done){
each(results, function(v, i){
v.addBoth(
function(v){
if(noResult){
noResult = false;
cancel(i);
x.callback(v);
}
}
);
});
}
return x;
};
};

async.select = function(cond, x){
// summary:
// Executes a condition, waits for it if necessary, and executes
// Nth function from list.
var fs = opts.call(x) == "[object Array]" ? x : aps.call(arguments, 1);
return function(init){
return new Deferred().addCallback(cond).addCallback(function(v){
if(typeof v == "number" && v >= 0 && v < fs.length){
return fs[v](init);
}else{
return new Error("async.select: out of range");
}
}).callback(init);
};
};

async.ifThen = function(cond, ifTrue, ifFalse){
// summary:
// Executes a condition, waits for it if necessary, and executes
// one of two functions.
return function(init){
return new Deferred().addCallback(cond).addCallback(function(v){
return (v ? ifTrue : ifFalse)(init);
}).callback(init);
};
};

async.loop = function(cond, body){
// summary:
// Executes a condition, waits for it if necessary, and executes
// the body, if truthy value was returned.
// Then it repeats the cycle until the condition function returns
// a falsy value.
return function(init){
var x, y = new Deferred(function(){ x.cancel(); });
function ifErr(v){ y.errback(v); }
function loop(v){
if(v){
x.addCallback(body);
x.addCallback(setUp);
}else{
y.callback(v);
}
return v;
}
function setUp(init){
x = new Deferred().
addCallback(cond).
addCallback(loop).
addErrback(ifErr).
callback(init);
}
setUp(init);
return y;
};
};
})();

/*
Design decisions:
seq() - behaves like the normal Deferred callback chain.
par() - if error, all pending Deferreds are cancelled and the error is signaled,
otherwise return an array of all results.
any() - just like par() but only one result is returned.
select() - any error is returned, otherwise the selected result is returned.
loop() - any error is returned, otherwise the last result is returned.
*/
41 changes: 41 additions & 0 deletions lang/async/event.js
@@ -0,0 +1,41 @@
dojo.provide("dojox.lang.async.event");

// Source of Deferred for events

(function(){
var d = dojo, event = dojox.lang.async.event;

event.from = function(src, name){
return function(){
var h, cancel = function(){
if(h){
d.disconnect(h);
h = null;
}
},
x = new d.Deferred(cancel);
h = d.connect(src, name, function(evt){
cancel();
x.callback(evt);
});
return x;
};
};

event.failOn = function(src, name){
return function(){
var h, cancel = function(){
if(h){
d.disconnect(h);
h = null;
}
},
x = new d.Deferred(cancel);
h = d.connect(src, name, function(evt){
cancel();
x.errback(new Error(evt));
});
return x;
};
};
})();
41 changes: 41 additions & 0 deletions lang/async/timeout.js
@@ -0,0 +1,41 @@
dojo.provide("dojox.lang.async.timeout");

// Source of Deferred for timeouts

(function(){
var d = dojo, timeout = dojox.lang.async.timeout;

timeout.from = function(ms){
return function(){
var h, cancel = function(){
if(h){
clearTimeout(h);
h = null;
}
},
x = new d.Deferred(cancel);
h = setTimeout(function(){
cancel();
x.callback(ms);
}, ms);
return x;
};
};

timeout.failOn = function(ms){
return function(){
var h, cancel = function(){
if(h){
clearTimeout(h);
h = null;
}
},
x = new d.Deferred(cancel);
h = setTimeout(function(){
cancel();
x.errback(ms);
}, ms);
return x;
};
};
})();
41 changes: 41 additions & 0 deletions lang/async/topic.js
@@ -0,0 +1,41 @@
dojo.provide("dojox.lang.async.topic");

// Source of Deferred for topics

(function(){
var d = dojo, topic = dojox.lang.async.topic;

topic.from = function(topic){
return function(){
var h, cancel = function(){
if(h){
d.unsubscribe(h);
h = null;
}
},
x = new d.Deferred(cancel);
h = d.subscribe(topic, function(){
cancel();
x.callback(arguments);
});
return x;
};
};

topic.failOn = function(topic){
return function(){
var h, cancel = function(){
if(h){
d.unsubscribe(h);
h = null;
}
},
x = new d.Deferred(cancel);
h = d.subscribe(topic, function(evt){
cancel();
x.errback(new Error(arguments));
});
return x;
};
};
})();

0 comments on commit f3e1fcd

Please sign in to comment.