Promises, and Asynchronous Javascript — Node.js

How do Javascript’s promises work?

Daniel Glover
4 min readJun 30, 2019

Promises in Javascript turn our applications into non-blocking applications. A non-blocking application, is an application that will be able to continue with operations whilst waiting for other operations to be completed.

Behind the scenes, we have the call stack. The call stack is a simple data structure (provided by the v8 Javascript engine), that is in charge of tracking the execution of our program and it does this by monitoring all the functions that are currently running. It is a synchronous programming language, and runs linearly.

An asynchronous program allows multiple things to happen at the same time, by using Node APIs & the callback queue/event loop.

When we run our program, the v8 engine provides us with the main() function which encapsulates and runs our application. As our functions are added to the stack and removed from the stack, some of those functions may be asynchronous. A commonly used example is setTimeout which is not part of the Javascript programming language, it is node.js that creates an implementation of setTimeout() using C++ and provides it to your node.js scripts to use. It’s an asynchronous way to wait a specific amount of time until the function inside runs. When we run setTimeout, it registers an event with Node APIs which consists of a waiting time and the callback provided is the function to run after the passing of that time. Whilst that allotted time is counting down, asynchronous programming allows us to do other stuff inside of the call stack.

Whilst Javascript is single-threaded, Node.js isn’t completely single-threaded. The code we run is single-threaded, however, Node uses other threads in C++ behind the scenes to manage your events. This allows us to continue running our application, whilst in the background we have another thread completing another task.

How do the callbacks from the Node APIs get executed?

With use of the callback queue and the event loop. The callback queue’s job is to track all of the callback functions that are ready to be executed. Once the given event is done from the Node API (timer is complete) that callback function is then added to the callback queue. However before it can be run it has to be moved to the callstack, as that is where the functions are executed. This is where the Event Loop comes in. The event loop looks at two things; callstack, and the callback queue. If the callstack is clear, it will run the items inside of the callback queue, if not, it will continue running the rest of the program.

What are promises (async and await), and what do they do?

Promises build on the callback pattern. Promises are an enhancement for callbacks, making asynchronous programming more manageable. To create a promise we can use the new operator with the Promise constructor function. Usually we wouldn’t have to worry about this, as they’d be created by the libraries we use. For example fetch returns a promise.

We make a request, and the request will return a promise, either a resolve or reject function. If its a resolve, we’ll get what we want, and if it’s a reject function returned, well something’s causing the function to run an error. We can produce good error handling by setting up our resolve and reject, like in this example…

Now, to make an asynchronous function, we add the async keyword right before the function declaration (e.g. const myFunc = async () => {} or async function myFunction() {}). With the addition of the async keyword, we’re also changing the behaviour of our program, as now that function will return a promise. If we were to explicitly set a return value inside our function to a string saying “Hi Earth”, we’d still be returning a promise, but it’d be a promise that gets fulfilled with the string “Hi Earth”. If we want the value, we can use .then to reach that value, or .catch for error handling.

With async functions we get the await operator, which gets used with a promise. So, inside our function we make a fetch call, and we can precede that code with the await operator (e.g. const response = await fetch(url)). It is what makes the async function truly asynchronous. When we run the function, it signifies to wait until the fetch call is finished and then continue running.

This example function fetches from a Dungeons & Dragons api, where the argument that gets passed as a string is interpolated into the url, which is stored in the response variable. I’m using await operator, as it inserts a suspension point until the task is complete. I store the promise in the response variable, and then parse the response to json, which is also an asynchronous task.

As you were.

--

--