-
Notifications
You must be signed in to change notification settings - Fork 29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Suppressed exceptions during dispose #63
Comments
Per the explainer, exceptions thrown during try {
await copy( 'first.txt', 'second.txt' );
} catch (e) {
console.log( e instanceof AggregateError ); // true
console.log( e.errors ); // [ Error('Some error'), Error('Device unavailable'), Error('Broken pipe') ]
} You can see an example of this in the transposed representation here: https://github.com/tc39/proposal-explicit-resource-management#try-using-with-explicit-local-bindings |
I suppose aggregation is optional? try {
await copy( 'first.txt', 'second.txt' );
} catch (e) {
let main, suppressed;
if ( e instanceof AggregateError ) {
main = e.errors[0];
suppressed = e.errors.slice(1);
} else {
main = e;
suppressed = [];
}
console.log( main ); // 'Some error'
console.log( suppressed ); // [ Error('Device unavailable'), Error('Broken pipe') ]
} |
Yes, aggregation happens purely based on whether more than one error is thrown (whether by the user or during cleanup), so it would be necessary to check the error. The issue with using a |
That seems confusing; why not always make it an aggregate error?
|
Scenario A - Exception in other code but no exception from dispose try {
using const x = new Disposable(() => {}); // does nothing, won't throw
throw new Error();
}
catch (e) {
console.log(e.name); // should this be Error or AggregateError?
} Scenario B - Exception in dispose but no exception from other code try {
using const x = new Disposable(() => { throw new Error(); }
// no other code that could error
}
catch (e) {
console.log(e.name); // should this be Error or AggregateError?
} Scenario C - Multiple exceptions in dispose but no exception from other code* try {
using const x = new Disposable(() => { throw new Error(); }
using const y = new Disposable(() => { throw new Error(); }
// no other code that could error
}
catch (e) {
console.log(e.name); // this should definitely be AggregateError
} Scenario D - Exception in dispose and in other code try {
using const x = new Disposable(() => { throw new Error("in dispose"); }
throw new Error("in other code");
}
catch (e) {
console.log(e.name); // What should this be?
} There are multiple ways of addressing these scenarios:
Currently, the proposal uses (1). (2) does not make sense to me if all of the disposals complete successfully. I might be convinced to consider (3), but there are issues with (4) I mentioned in my previous comment (i.e., in JS you can throw anything, including a frozen The only way I could see something like (4) working is via a meta-property that's only accessible in a |
... though if we did go the meta-property route, it would be nice to have other meta-properties that could be used in
I've had more than a few cases where I've written something like this: let ok = false;
try {
...
ok = true;
}
finally {
if (!ok) { /* exception was thrown */ }
} It would also be nice to have just a plain try {
...
}
catch {
// do something that cares about the fact we failed but doesn't need the error itself
throw; // rethrow unmodified exception
} |
I would expect A to be A metaproperty wouldn't work because thrown exceptions can also reject promises, and a rejected promise should have access to the same newly-available information that a catch block does. |
So, your suggestion would be to wrap everything in an
Fair enough, though I still might consider proposing the other metaproperties and |
Yes, that would be my suggestion. I suppose a |
I can make that change. I do have a related question though. In
Would that match your intuition? |
I think so? In the case when a body error and disposal error are both thrown, i'd hope that all of those errors are available somehow on the aggregate error - perhaps the body error could be the |
Having both try {
using const x = new Dispose(() => { throw Error("in dispose"); });
throw Error("in body");
}
catch (e) {
e.cause; // `Error("in body")` (?)
e.errors; // `[Error("in dispose")]` or `[Error("in body"), Error("in dispose")]`?
}
try {
using const x = new Dispose(() => { throw Error("in dispose"); });
}
catch (e) {
e.cause; // `undefined` (?)
e.errors; // `[Error("in dispose")]`
} Error |
In my suggestion, the body error was in In other words, I think it would either be both of those examples, or, the body error would be the first item in In other words, it seems cleaner to me to somehow differentiate the 0 or 1 body error, from the 0 or N disposal errors, in a more explicit way than "maybe it's the 0th item?" |
My source of concern is current expectations around how |
Right - in the case where both the body and disposal throws (the other cases are easy), the choices are:
The third seems very bad, the second seems suboptimal, the first is unfortunately awkward, and |
I went with Fixed in 7968c49 |
Imagine that as you catch the main exception, you get more exceptions during implicit calls to
dispose
methods (ex. remote filestystem has been lost):In Java, implicit exceptions are suppressed and added to the main. Is a similar solution planned here?
The text was updated successfully, but these errors were encountered: