summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Geisendörfer <felix@debuggable.com>2010-01-19 22:27:55 +0100
committerRyan Dahl <ry@tinyclouds.org>2010-01-19 14:29:57 -0800
commitbfd31448617dc4d66f6de5ced7c260562e01349f (patch)
treed5e3dc5f73e640a8cca0d3505410cef4708e22fb
parentf64371fccb61232be00534d6e9ecc9d73cbcb790 (diff)
downloadnode-bfd31448617dc4d66f6de5ced7c260562e01349f.tar.gz
Make unhandled Promise errors throw an exception
A promise will throw an exception unless an error handler is attached in the same "tick" that the error is emitted. This is to avoid silent promise failures.
-rw-r--r--doc/api.txt42
-rw-r--r--src/node.js27
-rw-r--r--test/mjsunit/test-promise.js23
3 files changed, 79 insertions, 13 deletions
diff --git a/doc/api.txt b/doc/api.txt
index 688bf6804..e72fbe51c 100644
--- a/doc/api.txt
+++ b/doc/api.txt
@@ -304,7 +304,47 @@ If you created the promise (by doing +new events.Promise()+) then call
the moment due to a bug; use +emitSuccess+ instead.)
+promise.emitError(arg1, arg2, ...)+ ::
-Emits the +"error"+ event.
+Emits the +"error"+ event. If a no error handler is attached to the promise
+between +promise.emitError()+ and +process.nextTick()+, an exception is
+thrown.
++
+To explain the exception behavior, assume you have a "computeQuestion"
+function as follows:
++
+----------------------------------------
+var events = require('events');
+function computeQuestion(answer) {
+ var promise = new events.Promise();
+ if (answer !== 42) {
+ promise.emitError('wrong answer');
+ return promise;
+ }
+ // compute the question for 42
+
+ return promise;
+}
+----------------------------------------
++
+You can stop an exception to be thrown here by attaching an errback handler
+right away (in the same event loop tick) like this:
++
+----------------------------------------
+computeQuestion(23).addErrback(function() {
+ // No exception will be thrown
+});
+----------------------------------------
++
+However, if you try to attach the error handler in a later tick, the promise
+will already have thrown an exception:
++
+----------------------------------------
+var promise = computeQuestion(23);
+setTimeout(function() {
+ promise.addErrback(function() {
+ // This will never execute, the promise already threw an exception
+ });
+}, 1000);
+----------------------------------------
+promise.timeout(timeout = undefined)+ ::
If the +timeout+ parameter is provided, the promise will emit an +"error"+
diff --git a/src/node.js b/src/node.js
index 6fc0ea440..725291c44 100644
--- a/src/node.js
+++ b/src/node.js
@@ -289,24 +289,31 @@ var eventsModule = createInternalModule('events', function (exports) {
this._values = Array.prototype.slice.call(arguments);
this.emit.apply(this, ['error'].concat(this._values));
+
+ if (this.listeners('error').length == 0) {
+ var self = this;
+ process.nextTick(function() {
+ if (self.listeners('error').length == 0) {
+ throw new Error('Unhandled emitError: '+JSON.stringify(self._values));
+ }
+ });
+ }
};
exports.Promise.prototype.addCallback = function (listener) {
- if (!this.hasFired) {
- return this.addListener("success", listener);
+ if (this.hasFired) {
+ return listener.apply(this, this._values);
}
- listener.apply(this, this._values);
- return this;
+ return this.addListener("success", listener);
};
exports.Promise.prototype.addErrback = function (listener) {
- if (!this.hasFired) {
- return this.addListener("error", listener);
+ if (this.hasFired) {
+ listener.apply(this, this._values);
}
- listener.apply(this, this._values);
- return this;
+ return this.addListener("error", listener);
};
/* Poor Man's coroutines */
@@ -1010,10 +1017,6 @@ process.mainModule = createModule(".");
var loadPromise = new events.Promise();
process.mainModule.load(process.ARGV[1], loadPromise);
-loadPromise.addErrback(function(e) {
- throw e;
-});
-
// All our arguments are loaded. We've evaluated all of the scripts. We
// might even have created TCP servers. Now we enter the main eventloop. If
// there are no watchers on the loop (except for the ones that were
diff --git a/test/mjsunit/test-promise.js b/test/mjsunit/test-promise.js
index e042030c4..9b314ea56 100644
--- a/test/mjsunit/test-promise.js
+++ b/test/mjsunit/test-promise.js
@@ -9,6 +9,8 @@ var
a2: 1,
b1: 1,
b2: 1,
+ c1: 1,
+ d1: 1,
};
// Test regular & late callback binding
@@ -37,6 +39,27 @@ b.addErrback(function(value) {
expectedCallbacks.b2--;
});
+// Test late errback binding
+var c = new Promise();
+c.emitError(TEST_VALUE);
+c.addErrback(function(value) {
+ assert.equal(TEST_VALUE, value);
+ expectedCallbacks.c1--;
+});
+
+// Test errback exceptions
+var d = new Promise();
+d.emitError(TEST_VALUE);
+
+process.addListener('uncaughtException', function(e) {
+ if (e.name === "AssertionError") {
+ throw e;
+ }
+
+ expectedCallbacks.d1--;
+ assert.ok(e.message.match(/unhandled emitError/i));
+});
+
process.addListener('exit', function() {
for (var name in expectedCallbacks) {
var count = expectedCallbacks[name];