Resource management (eg. try-with-resources)

# Michał Wadas (7 years ago)

After I started writing code extensively using async-await I have noticed that I would like to have automated way of dealing with resource management.

Scope-level manage was proven (Python, C++, Java at least) to be intuitive and error proof.

Previous discussion on this topic: esdiscuss. org/topic/resource-management

Example syntax:

try with Expression as Identifier {

} catch... finally...

Symbol.open and Symbol.close methods would be called respectively on entering and leaving try block.

Plays well with both synchronous and asynchronous resource access.

Another option is to reuse with:

with(expression as identifier) {

}

Though it would be confusing to statement with wildly different semantics in strict mode.

# T.J. Crowder (7 years ago)

On Fri, Nov 10, 2017 at 3:00 PM, Michał Wadas <michalwadas at gmail.com> wrote:

After I started writing code extensively using async-await I have noticed that I would like to have automated way of dealing with resource management.

This is something I'd really like to see as well.

What's the motivation for having an open? Java's AutoCloseable just has close; one would normally be acquiring these from a constructor or function, which is effectively the open...?

Can you give a concrete example using the syntax you're looking at? E.g., were you really thinking of actually having with and as? If so, I'd rather steal more directly from Java (perhaps replacing their ; with ,):

try (a = giveMeAResource(), b = giveMeAnotherResource()) {
    // `a` and `b` are in scope here
} catch (e) {
    // ...`a` and `b` are not in scope here
} finally {
    // ...`a` and `b` are not in scope here
}

...where (off-the-cuff):

  • Both catch and finally are optional
  • a and b are constants scoped to the try block (no need for the keyword, but could require or allow it for clarity)
  • Trailing commas on the resource declaration/initializer list are fine
  • b[Symbol.close] is called first, then a[Symbol.close]
  • Both closes are called prior to the execution of code in the catch or finally (if any)
  • If the try block throws, that error is the primary error and any close calls that also throw are "suppressed errors" (more below)
  • If the try block doesn't throw, but any close calls do, the first close that fails becomes the primary error; any others that fail become suppressed errors

Suppressed error handling:

  • If the primary error is an Error object, suppressed errors are added to a suppressed array property on it (creating it if necessary)
  • If the primary error is not an Error object, they're lost other than being treated as unhandled errors by the environment (for instance: logged, perhaps causing script termination; thought required on that)

-- T.J. Crowder

# Isiah Meadows (7 years ago)

Here's a C++-inspired alternative:

  • let res = value[Symbol.resource]() - Get an optional promise to a resource. lf missing, res is set to value instead.
  • res[Symbol.close]() - Close a resource, optionally returning a promise.
  • try let res = value - Get the resource by invoking value[Symbol.resource]() (or using value if the method is missing), assign it to res, and invoke res[Symbol.resource]() once it leaves the scope, even on error.
  • try await let res = value - Same as above, but await value[Symbol.resource]() (if necessary) and res[Symbol.close]() after calling them.
  • try with expr, try await with expr - Same as above, but without declaring a binding. (Works well with transactions, locks, etc.)
  • Close exceptions replace thrown exceptions.

Benefits:

  1. Less nesting, more concise.
  2. Java's try-with-resources is clunky and awkward. Python's with statement is no different.
  3. It works well with sync and async resources.
  4. User-level primitives are easier to write.

Of course, in the spec, there's a few logical cases where you'd want these methods to be defined/used:

  • %IteratorPrototype%[@@close]() - return this.close() if such a method exists.
  • %AsyncIteratorPrototype%[@@close]() - Similar to above.
  • Atomics.lock(sab, index, size) - Return a resource that acquires a lock for this section, and releases on @@close.