Skip to content

Instantly share code, notes, and snippets.

@fwg
Created November 22, 2009 18:20
Show Gist options
  • Save fwg/240655 to your computer and use it in GitHub Desktop.
Save fwg/240655 to your computer and use it in GitHub Desktop.
diff --git a/src/node.js b/src/node.js
index 6882102..e9b5905 100644
--- a/src/node.js
+++ b/src/node.js
@@ -314,7 +317,91 @@ process.Promise.prototype.wait = function () {
return ret;
};
+process.Promise.prototype.depend = function () {
+ var slice = Array.prototype.slice;
+ var deps = slice.call(arguments);
+ var results = Array(deps.length);
+ var promise = this;
+ function success (p, i) {
+ return function () {
+ deps.splice(deps.indexOf(p), 1);
+ results[i] = slice.call(arguments);
+ if(!deps.length) {
+ promise.emitSuccess.apply(promise, results);
+ }
+ };
+ }
+ function error () {
+ promise.emitError.apply(promise, arguments);
+ }
+ function cancel () {
+ promise.emitCancel.apply(promise, arguments);
+ }
+
+ for(var i = 0, dep; dep = deps[i]; i++){
+ dep.addCallback(success(dep, i))
+ .addErrback(error)
+ .addCancelback(cancel);
+ }
+ return this.addCallback(function () {
+ if(deps.length) {
+ throw new Error('Promise emitted success despite some unresolved dependencies.');
+ }
+ });
+}
+
+process.Promise.prototype.group = function () {
+ var slice = Array.prototype.slice;
+ var deps = slice.call(arguments);
+ var results = Array(deps.length);
+ var promise = this;
+ var had_error = false;
+ function success (p, i) {
+ return function () {
+ deps.splice(deps.indexOf(p), 1);
+ results[i] = slice.call(arguments);
+ if(!deps.length) {
+ promise.emitSuccess.apply(promise, results);
+ }
+ };
+ }
+ function error (p) {
+ return function () {
+ had_error = true;
+ deps.splice(deps.indexOf(p), 1);
+ cancelRest();
+ promise.emitError.apply(promise, arguments);
+ };
+ }
+ function cancel (p) {
+ return function () {
+ if(had_error) {
+ return;
+ }
+ had_error = true;
+ deps.splice(deps.indexOf(p), 1);
+ cancelRest();
+ promise.emitCancel.apply(promise, arguments);
+ };
+ }
+ function cancelRest () {
+ had_error = true;
+ for(var i = 0, dep; dep = deps[i]; i++) {
+ dep.cancel();
+ }
+ }
+
+ for(var i = 0, dep; dep = deps[i]; i++){
+ dep.addCallback(success(dep, i))
+ .addErrback(error(dep))
+ .addCancelback(cancel(dep));
+ }
+ return this.addCallback(function () {
+ if(deps.length) {
+ throw new Error('Promise emitted success despite some unresolved dependencies.');
+ }
+ }).addErrback(cancelRest).addCancelback(cancelRest);
+}
// Signal Handlers
diff --git a/test/mjsunit/test-promise-depend.js b/test/mjsunit/test-promise-depend.js
new file mode 100644
index 0000000..121dafd
--- /dev/null
+++ b/test/mjsunit/test-promise-depend.js
@@ -0,0 +1,87 @@
+process.mixin(require('./common'));
+
+var testFile1 = path.join(fixturesDir, "x.txt");
+
+var promise = new process.Promise();
+promise.depend(posix.cat(testFile1), posix.cat(testFile1));
+promise.addCallback(function(args1, args2) {
+ assertEquals(args1[0], args2[0]);
+});
+
+
+var err404;
+var nofile = './nofile'+Math.random()*100;
+try{
+ posix.cat(nofile).wait();
+}catch(e){
+ err404 = e.message;
+}
+var errPromise = new process.Promise();
+errPromise.addErrback(function(e) {
+ assertInstanceof(e, Error);
+ assertEquals(err404, e.message);
+});
+errPromise.addCallback(function() {
+ assertUnreachable('promise should not succeed with random filename');
+});
+
+errPromise.depend(posix.cat(nofile));
+
+
+var timeoutPromise = new process.Promise();
+timeoutPromise.addCallback(function () {
+ assertUnreachable('promise should timeout before it succeeds');
+});
+timeoutPromise.addErrback(function (e) {
+ assertEquals('timeout', e.message);
+});
+timeoutPromise.timeout(150);
+
+var tpromise = new process.Promise();
+setTimeout(function() {
+ tpromise.emitSuccess(true);
+}, 200);
+tpromise.addCallback(function(p) {
+ assertTrue(p);
+});
+
+timeoutPromise.depend(tpromise);
+
+
+var timeoutDepPromise = new process.Promise();
+timeoutDepPromise.addCallback(function () {
+ assertUnreachable('dependency should timeout.');
+});
+timeoutDepPromise.addErrback(function (e) {
+ assertEquals('timeout', e.message);
+});
+
+var tpromise = new process.Promise();
+tpromise.timeout(100);
+
+timeoutDepPromise.depend(tpromise);
+
+
+var cancelPromise = new process.Promise();
+var tpromises = [new process.Promise, new process.Promise];
+
+cancelPromise.depend.apply(cancelPromise, tpromises);
+
+tpromises.map(function(p){
+ return p.addCancelback(function(e){
+ assertUnreachable('the dependencies should not receive a cancel event');
+ }).addCallback(function(p){
+ assertTrue(p);
+ });
+});
+
+setTimeout(function(){
+ tpromises.forEach(function(p){
+ p.emitSuccess(true);
+ });
+}, 200);
+
+setTimeout(function(){
+ cancelPromise.cancel();
+}, 100);
+
diff --git a/test/mjsunit/test-promise-group.js b/test/mjsunit/test-promise-group.js
new file mode 100644
index 0000000..9d1d903
--- /dev/null
+++ b/test/mjsunit/test-promise-group.js
@@ -0,0 +1,86 @@
+process.mixin(require('./common'));
+
+function unreachable() {
+ asserUnreachable('grouped promises should all fail.');
+}
+function isTimeout(e) {
+ assertEquals('timeout', e.message);
+}
+function reached(counter){
+ counter.inc();
+ return function() {
+ counter.dec();
+ }
+}
+function Counter(){
+ this.count = 0;
+}
+Counter.prototype.inc = function () {
+ this.count++;
+}
+Counter.prototype.dec = function () {
+ this.count--;
+}
+function AssertReached(count, counter, msg) {
+ return function () {
+ assertTrue(count === counter.count, msg+" | counter is "+counter.count);
+ };
+}
+
+var gPromises = [];
+var cancel = new Counter;
+for(var i=5,p;i--;){
+ p = new process.Promise();
+ p.addCallback(unreachable)
+ .addErrback(unreachable)
+ .addCancelback(reached(cancel));
+ gPromises.push(p);
+}
+var promise = new process.Promise();
+promise.group.apply(promise, gPromises);
+promise.timeout(100);
+
+process.addListener("exit", AssertReached(0, cancel, "not all gPromises were cancelled"));
+
+
+gPromises.splice(0);
+var cancel2 = new Counter;
+for(i=4;i--;){
+ p = new process.Promise();
+ p.addCallback(unreachable)
+ .addErrback(unreachable)
+ .addCancelback(reached(cancel2));
+ gPromises.push(p);
+}
+p = new process.Promise();
+p.timeout(100);
+p.addCallback(unreachable).addCancelback(unreachable).addErrback(isTimeout);
+gPromises.push(p);
+
+promise = new process.Promise();
+promise.group.apply(promise, gPromises);
+
+process.addListener('exit',
+ AssertReached(0, cancel2, "timeout of a group promise did not cancel all promises"));
+
+
+gPromises.splice(0);
+var success = new Counter;
+for(i=5;i--;){
+ p = new process.Promise();
+ setTimeout((function(p){
+ return function(){p.emitSuccess()};
+ })(p), Math.floor(Math.random()*100));
+ p.addCancelback(unreachable)
+ .addErrback(unreachable)
+ .addCallback(reached(success));
+ gPromises.push(p);
+}
+promise = new process.Promise();
+promise.group.apply(promise, gPromises);
+promise.addCancelback(unreachable).addErrback(unreachable)
+ .addCallback(reached(success));
+
+process.addListener("exit", AssertReached(0, success, "all grouped promises should succeed"));
+
+
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment