Welcome to yet another entry in my Practical Promises series! We’re nearing the end, but I overlooked one important, and useful, capability of several promise libraries, which is what we’ll talk about today!
[more]
For Those Just Tuning In…
If you are just joining us, here is what you have missed so far:
In part 1, we talked about what promises are and what they can be used for.
In part 2, we started looking at how we can create promises.
Then in part 3, we saw how each call to then actually makes a new promise, and that those promises can be chained together.
In part 4, we learned how to combine promise chaining with the creation of new promises in order to simplify complex async workflows.
In part 5, we applied everything we have learned so far to create a nice, clean API that unwraps a complex result object via promise chaining.
In part 6, we explored what happens to our promises when we call then
and catch
in different orders.
Are we final
ly done yet?
We know that promises can represent a value that might be available at some point in the future…
const promise = Promise.resolve(1);
And we know that we can register a callback to be executed when/if a value becomes available using then
….
promise.then(x => console.log(`Got a value: ${x}`));
And we also know that we can register a callback to be executed if something goes wrong, and the value will never be available:
promise.catch(err => console.log(`There was a problem: ${err}`));
The then
and catch
instance functions are all that standard ES6/ES2015 promises provide. However, many common JavaScript promise libraries expose an additional function called finally
. This is true of AngularJS’s $q service, q itself, BlueBird, and probably others, too.
As you might expect, finally
in these libraries allows us to register a callback that is executed if the promise is resolved or rejected. The usage is pretty straight forward:
Note: If you want to follow along, you’ll need to make sure you have Q loaded up from https://cdnjs.cloudflare.com/ajax/libs/q.js/1.5.0/q.min.js. If you want to follow along your browser’s JavaScript console, you can execute this:
const e = document.createElement('script'); e.src = 'https://cdnjs.cloudflare.com/ajax/libs/q.js/1.5.0/q.min.js'; document.body.appendChild(e);
const resolvedPromise = Q.resolve('resolved!');
promise.finally(() => console.log(`We are finally here!`));
const rejectedPromise = Q.reject('rejected!');
promise.finally(() => console.log(`We still got here!`));
In my experience, finally
is typically used to perform cleanup after an async action completes. For example, you might want to clear a "loading" flag once an API call to retrieve data has completed, even if that call fails.
Emulating finally
in ES6
Unfortunately, standard ES6 promises do not expose a finally
function. The good news is that we can sort of emulate one by leveraging promise chaining.
As we’ve learned, calling catch
actually creates a new promise. That new promise is actually resolved with whatever our catch
returns (or undefined if our catch doesn’t return anything). So, we can achieve the same then
–catch
–finally
pattern by doing this:
const promise = Promise.reject('rejected!');
promise.then(() => console.log('This will never be executed...'))
.catch(() => console.log('We are in our catch...'))
.then(() => console.log('And now we are in our second then, which is acting like a finally...'));
Output:
We are in our catch...
And now we are in our second then, which is acting like a finally...
It isn’t as easy-to-read as finally
, but it does work.
finally
is NOT Standard!
The important take away from this post is that finally
is not standard. There’s a good chance whatever framework you are using might expose finally
, but if you want to use the standard Promise
object, you’ll have to stick to then
and catch
:
doSomeAsyncAction().then(resp => /* do something with the result */)
.catch(err => /* do something with the error... */)
.then(() => /* do any final cleanup */);
Up Next…
I think we’ve about beaten this Promise horse to death. All that remains now is to look at how we can combine promises with the new ES7 async/await capabilities that are Coming Soon to a browser near you!