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.
This commit is contained in:
parent
f64371fccb
commit
bfd3144861
42
doc/api.txt
42
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"+
|
||||
|
27
src/node.js
27
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
|
||||
|
@ -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];
|
||||
|
Loading…
x
Reference in New Issue
Block a user