diff --git a/doc/api.txt b/doc/api.txt index 9f0c79d566e..8f2ce20035a 100644 --- a/doc/api.txt +++ b/doc/api.txt @@ -215,6 +215,7 @@ emit anymore events. | Event | Parameters | Notes | +"success"+ | (depends) | | +"error"+ | (depends) | +| +"cancel"+ | (depends) | |========================================================= +promise.addCallback(listener)+ :: @@ -223,6 +224,9 @@ Adds a listener for the +"success"+ event. Returns the same promise object. +promise.addErrback(listener)+ :: Adds a listener for the +"error"+ event. Returns the same promise object. ++promise.addCancelback(listener)+ :: +Adds a listener for the +"cancel"+ event. Returns the same promise object. + +promise.emitSuccess(arg1, arg2, ...)+ :: If you created the promise (by doing +new node.Promise()+) then call +emitSuccess+ to emit the +"success"+ event with the given arguments. @@ -233,10 +237,21 @@ the moment due to a bug; use +emitSuccess+ instead.) +promise.emitError(arg1, arg2, ...)+ :: Emits the +"error"+ event. ++promise.emitCancel(arg1, arg2, ...)+ :: +Emits the +"cancel"+ event. You may still get a +"success"+ or +"error"+ +callback if the promise giver does not handle the cancel event. Use ++promise.cancel()+ to ignore any later events. + ++promise.cancel()+ :: +Clears all +"success"+ and +"error"+ event listeners from the promise, then +emits the +"cancel"+ event. Whether or not the promise is actually canceled +or not depends on the promise giver. This also clears Promise.timeout() if one +was set. + +promise.timeout(timeout = undefined)+ :: If the +timeout+ parameter is provided, the promise will emit an +"error"+ event after the given amount of millseconds. The timeout is canceled by any -+"success"+ or +"error"+ event being emitted by the Promise. ++"success"+, +"error"+ or +"cancel"+ event being emitted by the Promise. + To tell apart a timeout from a regular "error" event, use the following test: + diff --git a/src/events.js b/src/events.js index e145649dc64..e26d6de0e6c 100644 --- a/src/events.js +++ b/src/events.js @@ -30,14 +30,42 @@ node.Promise.prototype.timeout = function(timeout) { clearTimeout(this._timer); } + var promiseComplete = false; + var onComplete = function() { + promiseComplete = true; + }; + + this + .addCallback(onComplete) + .addCancelback(onComplete) + .addErrback(onComplete); + var self = this this._timer = setTimeout(function() { + if (promiseComplete) { + return; + } + self.emitError(new Error('timeout')); }, this._timeoutDuration); return this; }; +node.Promise.prototype.cancel = function() { + this._events['success'] = []; + this._events['error'] = []; + + this.emitCancel(); +}; + +node.Promise.prototype.emitCancel = function() { + var args = Array.prototype.slice.call(arguments); + args.unshift('cancel'); + + this.emit.apply(this, args); +}; + node.Promise.prototype.addCallback = function (listener) { this.addListener("success", listener); return this; @@ -48,6 +76,11 @@ node.Promise.prototype.addErrback = function (listener) { return this; }; +node.Promise.prototype.addCancelback = function (listener) { + this.addListener("cancel", listener); + return this; +}; + node.Promise.prototype.wait = function () { var ret; var had_error = false; diff --git a/test/mjsunit/test-promise-timeout.js b/test/mjsunit/test-promise-timeout.js index 39d188797bb..9bf5e195336 100644 --- a/test/mjsunit/test-promise-timeout.js +++ b/test/mjsunit/test-promise-timeout.js @@ -29,6 +29,55 @@ try { timeouts++; } +var successPromise = new node.Promise(); +successPromise.timeout(500); +setTimeout(function() { + successPromise.emitSuccess(); +}, 250); + +successPromise.addErrback(function() { + assertUnreachable('addErrback should not fire if there is no timeout'); +}); + +var errorPromise = new node.Promise(); +errorPromise.timeout(500); +setTimeout(function() { + errorPromise.emitError(new Error('intentional')); +}, 250); + +errorPromise.addErrback(function(e) { + assertInstanceof(e, Error); + assertEquals('intentional', e.message); +}); + +var cancelPromise = new node.Promise(); +cancelPromise.timeout(500); +setTimeout(function() { + cancelPromise.cancel(); +}, 250); + +setTimeout(function() { + cancelPromise.emitSuccess('should be ignored'); +}, 400); + +cancelPromise.addCallback(function(e) { + assertUnreachable('addCallback should not fire if the promise is canceled'); +}); + +cancelPromise.addErrback(function(e) { + assertUnreachable('addErrback should not fire if the promise is canceled'); +}); + +var cancelTimeoutPromise = new node.Promise(); +cancelTimeoutPromise.timeout(500); +setTimeout(function() { + cancelPromise.emitCancel(); +}, 250); + +cancelPromise.addErrback(function(e) { + assertUnreachable('addErrback should not fire after a cancel event'); +}); + process.addListener('exit', function() { assertEquals(2, timeouts); }); \ No newline at end of file