Go style defer

Adding a defer keyword like what we do in Golang should be super useful when it comes to multiple returns in different branches in a function which requires cleanup before each return.
I'm curious about why no one has came up with this idea or just I didn't find one...

Example: https://gobyexample.com/defer

Benefits:

  1. No need to write the same cleanup code multiple times before each return
  2. Handles branches more flexible than try-catch-finally, which could automatically call the only needed deferred functions on the corresponding return
  3. Automatically cleanup on errors
  4. etc...

Another way of achieving this could be adding destructors to classes like in C++. Using destructors could simulate defer flawlessly: https://stackoverflow.com/questions/48117908/is-the-a-practical-way-to-emulate-go-language-defer-in-c-or-c-destructors, but that should be another topic

You may be interested in this Stage 2 proposal https://github.com/tc39/proposal-explicit-resource-management

Which provides something like this:

function openFile(path) {
   let file = ...;
   return {
     data: file,
     [Symbol.dispose]() {
       file.close();
     } 
   } 
}

try using (const f = openFile()) {
    // read file in here
    ... 
} // file closed

I've noticed this proposal and find it way too complicated than just a single defer keyword. So I created this topic here.

It might be worth creating an issue on that proposal too, you may got more replies than on here.

Have you read though this particular issue on that proposal?

In that thread using approach is very similar to defer, but the function to call is implicit.

// instead of this:
{
  const x = getFile();
  defer x[Symbol.dispose]();
  ...
} // x[Symbol.dispose] called
// write this:
{
  using const x = getFile();
  ...
} // x[Symbol.dispose] called

I think this should be linked to the destructor idea.

And it's still something different: while in Go, we can defer any function, which could be cleanup or some other functionalities. In this particular proposal, it focuses only on one object, which acts more like destructors in other languages like C++.

You could still defer any function like this:

function defer(fn) {
  return { [Symbol.dispose]: fn };
}

{
  using defer(() => console.log('clean up'));
  ...
} // logs 'clean up'

Seems legit, so it's still a β€œdestructor” emulated defer.

JS already has try/finally (in contrast to Go), so it doesn't need the defer functionality. Sure, I like Go's approach, but there's no need to have two mechanism that do more or less the same thing. You can even emulate Go's behaviour quite easily:

const defers = [];
try {
  if (…) {
    const res = getResource();
    defers.push(() => res.dispose();
    …
  }
  if (…) {
    if (…) {
      return
    }
    const file = open();
    defers.push(() => file.close());
    …
  }
} finally {
  for (cleanup of defers.reverse()) {
    cleanup();
  }
}

Sure enough we can achieve the same functionality using try/finally, but just like we already have callbacks and then we have Promise then async await, simplifying the procedure of doing the same thing and plus we get the benefit of writing more organized code should be a good thing to happen.