Finally and returning, a strange couple.

Intro

I took a little bit of pause from the 160 questions for Data Science to write a few posts about the magic world of JavaScript. This will be the last post before returning to the Data Science questions list.

And might I add: finally! Also because this post will focus on this useful but strange part of JavaScript: finally.

What is it

Finally is keyword that has two different meanings: it can be used in a try/catch/finally block or it can be used with a Promise in a then/catch/finally. While it has the same basic meaning in both the situations, there is a little but very important difference between the two.

In both cases finally is used to contain code that will be run no matter which path the previous code takes: either after the try/then or after the catch if an error is thrown. This means that the code we put in the finally block will always be run, even if there is no catch block and an exception is not caught.

This can be really useful in many situations, one of the most common ones being when something that is used inside the try/then block needs to be released or closed: I’ve found myself using the finally block to close connections to databases at the end of something, even if it failed.

The first strange part

finally has two things that I find really strange, the first one being the fact that it lacks the ability to know from which of the previous states the execution of the code arrives: once inside the finally block, there is no information on wheter the code has had an exception or it run without problems.

The second, strangest part

Consider the following code:

function foo(a, b) {
  console.log('Starting to compute...')
  try {
    const result = a + b
    if (result > 10) throw new Error('Value is way too high')
    return result
  } catch (err) {
    console.error(err)
    return Infinity
  } finally {
    console.log('Computation completed!')
    return
  }
}

This code is simple: it sums two values and throws if the value is higher than 10, returning Infinity, otherwise it returns the result. There is a problem though: if I were to try to run the following function:

const total = foo(3, 5)

The value of total is not 8 as I might expect, but undefined. That’s because every time the finally block contains a return, that’s what the whole try/catch/finally block will return. That’s unexpected, but what happens is that the whole try block is run, and after the return part of the try block, the finally block starts. You can simply check it by using an external variable and changing it during the return.

So don’t trust a finally block, it might be able to override your previous code and return whatever it pleases!

That said, there is something else that makes this behaviour really strange, this:

async function foo(a, b) {
  const result = a + b;
  if (result > 10) throw new Error("Value is way too high");
  return result;
}

function bar(a, b) {
  console.log('Starting to compute...')
  foo(a, b).then((result) => {
    return result
  }).catch((err) => {
    console.error(err)
    return Infinity
  }).finally(() => {
    console.log('Computation completed!')
    return
  })
}

This is the same code as before, only the calculation part is wrapped in an async function in order to return a promise. Also, I’m using then/catch/finally instead of try/catch/finally.

This time, if I were to try and run something like:

const value = await bar(2, 4)

The variable value would be === 6. In this case, while the finally block is run (we can check using the logs) just like before, its returning value is not used by the promise as return value.

This is strange, because while it looks like the same code as before, the behaviour is different. Always remember to check your finally block, and that its behaviour changes accordingly to the bigger block your using it into!