Created
November 22, 2009 18:20
-
-
Save fwg/240655 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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