JavaScript: use the finally clause in generators with caution

This is about the guarantees provided by the finally clause, and therefore about its suitability for critical resource cleanup tasks, especially after unexpected failures.

In 2013, Axel Rauschmeyer (love his material about JS on the web!) wrote

The finally clause is always executed, no matter what happens inside the try clause (return, exception, break, normal exit).

Mozilla MDN web docs say

Statements that are executed after the try statement completes. These statements execute regardless of whether an exception was thrown or caught.

Sounds like that’s a guarantee.

In Python, we love using the finally clause precisely because of its guarantees. The code you put in there is run, and that guarantee can be translated into an incredibly powerful and easy-to-reason-about technique for building solid resource cleanup and shutdown procedures. This is also true when used from within generators, as I have done for example here.

I am still new to the JavaScript ecosystem, and the question whether I can rely on the same level of guarantee in NodeJS/JavaScript came up. I found the following discussion to be incredibly insightful: https://esdiscuss.org/topic/garbage-collection-in-generators

Andreas Rosberg said about

function getResults*() {
     try {
         var resource = acquire();
         for(const item of resource) yield process(item);
     } finally {
         release(resource);
     }

that it is a

“bogus form of resource management”. This is an anti-pattern in ES6. It won’t work correctly. We should never have given the illusion that it does.

He added that

Python’s idea is just confused and crazy.

Benjamin Gruenbaum (@benjamingr) replied and was kind of representing my perspective in this discussion, saying that

try/finally is commonly used for resource management and garbage collection is a form of automatic resource management. There is also the assumption that finally is always run. Two other languages have opted into the “run finally” behavior so I wouldn’t call it crazy

and that

I’m very curious why languages like Python have chosen to call finally blocks in this case – this was not a hindsight and according to the PEP. They debated it and explicitly decided to call release.

The fundamental difference between the approaches might be as of garbage collection, seemingly, as Mark S miller says:

JavaScript GC is not specified to be precise, and so should be assumed conservative. […] C++ RAII and Python refcounting are completely different: they are precise, prompt, predictable, and deterministic.

but on the other hand Andreas Rossberg said

Try-finally has nothing to do with GC, it’s just control flow

I don’t think the discussion was concluded in a satisfying way, but I nevertheless enjoyed it very much (a big thank you to the protagonists in this discussion!).

My big takeaway was that in JavaScript one should not generally rely on the finally clause in generators, but instead one needs to carefully look at how exactly it is being used in every special case, and whether or not one should rely on its execution! That makes things difficult. Have I said that I like simplicity? Makes me a little sad, but good to know!

After all I also found in this explicit remark in a nice Mozilla blog post about ES6 generators (emphasis mine):

Note that the .return() is not called automatically by the language in all contexts, only in cases where the language uses the iteration protocol. So it is possible for a generator to be garbage collected without ever running its finally block.