So far in this async series, we’ve covered Node.js style callbacks, the Async module, and promises. In this final part of the series, we’ll learn about async functions (a.k.a. async/await). To me, async functions are the most exciting thing to happen to JavaScript since Ajax. Finally, we can read JavaScript code in a synchronous manner while it executes asynchronously as it always has.


Async functions overview

Async functions are a relatively new feature of JavaScript (not specific to Node.js). Support for the feature first landed in Node.js v7.6 via an update to the V8 JavaScript engine. Because async functions rely heavily on Promises, I recommend you read the previous post before continuing.

I like to think of async functions as two parts: async and await. Let’s look at each part in turn.


For the longest time, we’ve had the ability to create functions in JavaScript using function statements (must be named) or function expressions (often anonymous).

If you run the script above in Node.js, you should see 42 printed to the console.

JavaScript now has asynchronous counterparts to these constructs. Placing the new async keyword before the function statement or expression returns an AsyncFunction (async function) object.

Running this script in Node.js should print Promise { 42 }. As you can see, when async functions are invoked, they return promises rather than the actual values returned!

For the async based script to be the functional equivalent of the first, we’d have to rewrite it as follows.

Now we’re back to logging the value 42.

Just as we saw with promise chaining, if the async function completes without error, then the promise it returns is resolved. If the function returns a value, then that becomes the promise’s value. If an error is thrown and goes unhandled, then the promise is rejected and the error becomes the promise’s value.

Though interesting, returning promises isn’t what makes async functions special. We could, after all, just return promises from regular functions. What makes async functions special is await.


The await operator, which is only available inside of an async function, is where the magic happens. It’s like hitting the pause button on your code so that it can wait for a promise to be resolved or rejected before continuing. This is a concept known as a coroutine. Coroutines have been available in JavaScript since generator functions were introduced, but async functions make them much more approachable.

Await does not block the main thread. Instead, the currently running call stack, up to the point of await, is allowed to finish so that other functions in the callback queue can be executed. When the promise is resolved or rejected, the remaining portion of the code is queued for execution. If the promise was resolved, its value is returned. If the promise was rejected, the rejected value is thrown on the main thread.

Here’s a demonstration of await that uses setTimeout to simulate an async API. I’ve added some additional console output to help illustrate what’s happening.

When this script is run in Node.js without an error occurring, the output will look like the following (I’ve added a comment where the two-second delay happens).

Note that after async call was logged before after await 0.22454453163016597. Only the remaining code in the async function is paused; the remaining synchronous code in call stack will finish.

If an error is thrown, you’ll see the UnhandledPromiseRejectionWarning we covered in the last post. The rejection could be handled with the methods mentioned in that post or using try…catch!


In the first post in this series, I explained why try…catch blocks don’t work with asynchronous operations – you can’t catch errors that occur outside of the current call stack. But now that we have async functions, try…catch can be used for asynchronous operations!

Here’s a stripped down version of the previous script that catches errors that occur in the async API and uses a default value instead.

If you run that script enough times you’ll eventually get 42 in the output. try…catch works again, woohoo!

Async loops

In addition to being able to use try…catch blocks again, we can do asynchronous loops too! In the following example, I use a simple for loop that logs out three values serially.

Running this script in Node.js, you should see three numbers printed to the console every two seconds. No third party libraries, no complicated promise chains, just a simple loop. Loops work again, yay!

Parallel execution

Clearly, async functions make it easy to do sequential flows and use standard JavaScript constructs with asynchronous operations. But what about parallel flows? This is where Promise.all and Promise.race come in handy. Because they both return promises, await can work with them like any other promise-based API.

Here’s an example that uses Promise.all to get three random numbers in parallel.

Because Promise.all rejects its promise if any promise passed in is rejected, you may need to run the script a few times to see the three random numbers printed out.

Async function demo app

The async function demo app is comprised of the following four files. The files are also available via this Gist.

This is a very basic package.json file. The only external dependency is oracledb.

All of the async methods in node-oracledb are overloaded to work with callback functions or promises. If a callback function is not passed in as the last parameter, then a promise will be returned. This version of the index.js uses the await operator with the driver’s promise APIs to create a connection pool and fetch an employee. Although the pool is returned from the call to createPool, it’s not referenced here as the built-in pool cache will be used in employees.js.

The db-config.js file is used in index.js to provide the connection info for the database. This configuration should work with the DB App Dev VM, but it will need to be adjusted for other environments.

This version of the employees module is similar to the promise version in that the getEmployee function was written as a promise-based API – it immediately returns a new promise instance which is asynchronously resolved or rejected. The main difference is that await is used with the driver’s promise APIs to get a connection to the database, use it to execute a query, and then close a connection.

A try…catch…finally block was used to catch errors and ensure the connection was closed either way. To me, this version of the module is the simplest to read of all those in the series and it doesn’t hurt that it has the fewest lines of code as well.

Hopefully, you now have a better grasp on async functions and are as excited as I am about using them!


  • Christoph says:

    Thanks for the DB connection posts, Dan. How do you best choose between callbacks, promises, and async database connections?

    • danmcghan says:

      Hi Christoph,

      You’re welcome! 🙂

      I think that consistent usage of async/await produces code that is much easier to read and maintain than what was possible before – everyone should move in this direction. However, you can’t just add these keywords to your code without understanding how they work.

      Because async/await builds on promises (in fact, AsyncFunctions return promises), a good understanding of promises is necessary to use async/await correctly. And because promises leverage callbacks, a good understanding of those is needed too. Think of it as a progression: you have to crawl, walk, and then run. Once you can run, that’s probably the best way to get around, but your knowledge of crawling and walking will remain very useful!

      Having said all that, knowledge of the Async module is still useful for certain scenarios. 🙂

      Here’s a link to a talk I recently did on this topic:

      And here’s a link to a list of some really great talks I learned a lot from:

  • Ziv says:

    Thanks for this post.

    In a web site, should the createPool be called on the app startup and the getConnection / close be called for each request? or it must be per sql call like in your example?

    • danmcghan says:

      Thanks for your question. The focus of this post was on the use of async/await to work with connections. In the context of a website or even an API, you generally want to keep the connection open for an entire request or transaction, not just a single SQL execution.

      However, if you’re executing multiple SQL statements without doing much mid-tier data processing between them, you should consider shipping the statements over as a PL/SQL block so you only have one network roundtrip. I show an example of this in my API series. Note that most of the transactions are just a single statement. But the last one, the delete, requires two statements so I ship them over as a very simple PL/SQL block.

Leave a Reply