-
Notifications
You must be signed in to change notification settings - Fork 27k
Description
I have an application that performs validation on mouse events. If validation fails, it throws an Error. We catch thrown errors in our application by doing:
window.addEventListener('error', (event) => { handleUncaughtError(event, logger); });
This worked fine: task.invoke() in the below code would call into our code, which would throw an error and would be caught by the above listener.
var invokeTask = function (task, target, event) {
// for better performance, check isRemoved which is set
// by removeEventListener
if (task.isRemoved) {
return;
}
var delegate = task.callback;
if (typeof delegate === 'object' && delegate.handleEvent) {
// create the bind version of handleEvent when invoke
task.callback = function (event) { return delegate.handleEvent(event); };
task.originalDelegate = delegate;
}
// invoke static task.invoke
task.invoke(task, target, [event]);
var options = task.options;
if (options && typeof options === 'object' && options.once) {
// if options.once is true, after invoke once remove listener here
// only browser need to do this, nodejs eventEmitter will cal removeListener
// inside EventEmitter.once
var delegate_1 = task.originalDelegate ? task.originalDelegate : task.callback;
target[REMOVE_EVENT_LISTENER].call(target, event.type, delegate_1, options);
}
};
However, now the callsite for task.invoke() looks like:
var invokeTask = function (task, target, event) {
// for better performance, check isRemoved which is set
// by removeEventListener
if (task.isRemoved) {
return;
}
var delegate = task.callback;
if (typeof delegate === 'object' && delegate.handleEvent) {
// create the bind version of handleEvent when invoke
task.callback = function (event) { return delegate.handleEvent(event); };
task.originalDelegate = delegate;
}
// invoke static task.invoke
// need to try/catch error here, otherwise, the error in one event listener
// will break the executions of the other event listeners. Also error will
// not remove the event listener when `once` options is true.
var error;
try {
task.invoke(task, target, [event]);
}
catch (err) {
error = err;
}
var options = task.options;
if (options && typeof options === 'object' && options.once) {
// if options.once is true, after invoke once remove listener here
// only browser need to do this, nodejs eventEmitter will cal removeListener
// inside EventEmitter.once
var delegate_1 = task.originalDelegate ? task.originalDelegate : task.callback;
target[REMOVE_EVENT_LISTENER].call(target, event.type, delegate_1, options);
}
return error;
};
and it seems like the error is ignored in globalCallback()
function globalCallback(context, event, isCapture) {
// https://github.com/angular/zone.js/issues/911, in IE, sometimes
// event will be undefined, so we need to use window.event
event = event || _global.event;
if (!event) {
return;
}
// event.target is needed for Samsung TV and SourceBuffer
// || global is needed https://github.com/angular/zone.js/issues/190
var target = context || event.target || _global;
var tasks = target[exports.zoneSymbolEventNames[event.type][isCapture ? utils_1.TRUE_STR : utils_1.FALSE_STR]];
if (tasks) {
// invoke all tasks which attached to current target with given event.type and capture = false
// for performance concern, if task.length === 1, just invoke
if (tasks.length === 1) {
invokeTask(tasks[0], target, event);
}
else {
// https://github.com/angular/zone.js/issues/836
// copy the tasks array before invoke, to avoid
// the callback will remove itself or other listener
var copyTasks = tasks.slice();
var errors = [];
for (var i = 0; i < copyTasks.length; i++) {
if (event && event[IMMEDIATE_PROPAGATION_SYMBOL] === true) {
break;
}
var err = invokeTask(copyTasks[i], target, event);
err && errors.push(err);
}
var _loop_1 = function (i) {
var err = errors[i];
api.nativeScheduleMicroTask(function () {
throw err;
});
};
for (var i = 0; i < errors.length; i++) {
_loop_1(i);
}
}
}
}
IIUC, this is caused by the fix for this #41562 (comment)
This is a regression (I tested with a version from 3 weeks ago and it worked as expected).
Can we ensure that all caught errors are rethrown? Or whatever is the Angular way to undo this regression.