You know how try
and catch
work, but what about finally
? Does it run after rethrown exceptions or return
statements?
With the advent of async
/await
, I’ve recently found myself using a lot more try
/catch
/finally
in my code. But honestly, I’m a little out of practice with finally
. When I went to actually use it, I was a little unsure of its finer details. So I put together a few examples.
throw
a catch
Consider the case where you throw an exception within a catch
block. There’s nothing to catch your throw
before it exits the function. Does finally
run?
To find out, just uncomment the example()
call at the bottom of the editor!
The finally runs, even though the last log statement doesn’t!
You can see that finally
is a little special; it lets you run things between throwing an error and leaving the function, even if that error was thrown inside a catch
block.
Did you know that if you supply a finally
block, you don’t need to supply a catch
block too? You probably did, but it was worth asking!
So next question: does that finally
block get called even if nothing goes wrong in the try
block? If you’re not sure, uncomment the example()
at the bottom of the editor to find out.
Yep, finally
is called even when nothing goes wrong. Of course, it also gets called when something does go wrong.
That’s the idea behind finally
— it lets you handle both cases, as you can see in this example:
So finally lets you clean up after yourself when exceptions occur. But what about when nothing goes wrong and you just return
from the function like normal… in a try
block?
Take a look at the below example. Is it possible that the finally
block within example()
can run after you’ve already hit a return
statement? Uncomment the example()
at the bottom of the editor to find out!
The finally
block on a try
/catch
/finally
will always run — even if you bail early with an exception or a return
.
This is what makes it so useful; it’s the perfect place to put code that needs to run regardless of what happens, like cleanup code for error-prone IO. In fact, that’s what inspired this article.
finally
for…Frontend Armory is a statically rendered website; it’s built with a tool called Navi, which lets you configure a renderPageToString()
function that will be used to render each page.
In order to make sure each call to renderPageToString()
is independent of the previous one, Navi uses try
/catch
/finally
and some obscure node-fu to unload any modules that are loaded during the rendering process.
let oldCacheKeys = new Set(Object.keys(require.cache))
try {
html = await renderPageToString(options)
}
catch (e) {
throw e
}
finally {
process.env.NODE_ENV = nodeEnv
for (let key of Object.keys(require.cache)) {
if (!oldCacheKeys.has(key)) {
delete require.cache[key]
}
}
}
As you can see from the above example, try
/catch
/finally
also work great with JavaScript’s new async
/await
syntax. So if this has reminded you that you need to brush up on async
/await
, now’s the time to mosey on over to my Mastering Asynchronous JavaScript course!
But that’s it for today. Thanks so much for reading! If you have any questions or comments, shoot me a Tweet or DM at @james_k_nelson. And until next time, happy coding :-)
Tokyo, Japan