proposal-explicit-resource-management: Suppressed exceptions during dispose

Imagine that as you catch the main exception, you get more exceptions during implicit calls to dispose methods (ex. remote filestystem has been lost):

async function copy(first, second) {
  using(let firstFile = await open(first)) {
    using(let otherFile = await open(second)) {
      if( someCondition ) {
        throw new Error( 'Some error' );
      }      
    }
  }
}

try {
  await copy( 'first.txt', 'second.txt' );
} catch (e) {
  console.log( e.message ); //  'Some error'
  console.log( e.suppressed ); // [ Error('Device unavailable'), Error('Broken pipe') ]
}

In Java, implicit exceptions are suppressed and added to the main. Is a similar solution planned here?

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 16 (6 by maintainers)

Most upvoted comments

I went with AggregateError where errors are the errors from dispose and cause is the body error (if present). If there are no errors from dispose, the body error is thrown directly.

Fixed in 7968c49e482dcf40a9a942b9ad8aaf26daf0240e

My source of concern is current expectations around how AggregateError generally works. If this were a separate error I would probably be less concerned. I think I am fine with the body error being cause however, since this is new syntax and MDN or other sites could document what to expect.

… though if we did go the meta-property route, it would be nice to have other meta-properties that could be used in finally as well:

  • throw.hasErrortrue in a catch clause, or in a finally block if propagating an uncaught error; false any other time.
    • Alternatives: .errored, .hasFault, .faulted, .hasException, .excepted, .wasThrown, etc.
  • throw.error — The thrown error that caused us to jump to catch or finally (if there was one).
    • Alternatives: .fault, .exception, .thrown, .cause, .reason, etc.
  • throw.suppressed — An AggregateError for any errors thrown during disposal.

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 throw; statement in a catch clause that rethrows the caught exception (C# has this, not sure about other languages offhand). Might be worth a separate proposal, and I’ve been thinking about it since we added bindingless catch:

try {
  ...
}
catch {
  // do something that cares about the fact we failed but doesn't need the error itself
  throw; // rethrow unmodified exception
}

That seems confusing; why not always make it an aggregate error?

Promise.allSettled doesn’t skip the AggregateError when there’s only one rejection; that forces consumers to duck-type and have two code paths.