A Brief Overview of Order of Execution in JavaScript

(Shoutout to Lauren Greenberg for co-presenting the Technical Talk this was inspired by!)

JavaScript is a single-threaded, asynchronous language. What does this mean?

Single threaded languages make use of only one CPU at a time, meaning only one thing can happen at a time. Other languages are multi-threaded, meaning that many processes can happen at the same time. However, since those processes are often inter-related or inter-dependent, it requires a great deal of communication between the processors. If that communication happens incorrectly or out of order, the system can crash.

Synchronous refers to things happening in a specific order. In a relay race, the first person has to finish and hand off the baton (or reach the end of the pool, etc.) before the next person can start.

Asynchronous means that a process can happen out of order. How is that possible? A process can be started, put to the side, and resumed later, or a process can be running in the background, such as when the process is waiting for a response from an external source.

The way this works in JavaScript is through the event loop!

What is the Order of Execution?

Order of execution in JavaScript is dependent on the following components working together to pass and order information.

  • The Callstack
  • The Event Loop
  • The Task Queue
  • WebAPIs/External Resources

We can think through the order of execution using the (sometimes headache inducing) example of the asynchronous fetch request.

A very simple fetch request might look something like this

fetch(url)
.then(res => res.json())
.then(json => {
this.setFetchResults(json.items)
}

You fetch some data, you parse to json, and then you pass that json into another function and do something with it. Fairly straightforward.

Now, if this fetch request happens as part of a larger function, at the beginning of that function, JavaScript will essentially put it to the side, and wait for the response (let’s say in this case from an external API), and continue moving down the function.

Eventually, hopefully, there is either a response from the external API (whether an error, or the data we’re expecting) or a set timeout. Then, this information is passed to the task queue.

As the call stack is cleared, the Event Loop passes information from the task queue back to the call stack to finish executing the function.

A Restaurant Analogy

A good way to piece this all together is to think about a restaurant kitchen. The chef, the sous chefs, prep cooks, pastry chefs, and porters, all have different functions within a restaurant kitchen. They all have individual tasks that they are responsible for as the food is passed from one station to the next, and they can only do one thing at a time. That said, no one by the panini press and watches your grilled cheese toast without doing anything else. If you’ve ever worked on the line in a kitchen, you know that this would get you fired immediately. You make and start the grilled cheese, put it on, and immediately get going on the next order or two, while probably also tidying up your station, restocking, and helping out the people around you.

A restaurant would be so slow if they prepared everything one step at a time for one customer that no one would support their business. The reason that a well-managed restaurant is able to function is because of a delicate balance of taking one task at a time, but always having a queue of things to do. Everyone knows what their jobs are and balances tasks that take a longer amount of time with those that can be done quickly and in preparation for what is next.

Similarly, managing asynchronous functions in JavaScript well can greatly improve user experience. Using asynchronous functionality can reduce lag times. We can also alter the UI to pass information only when it is ready, and build our programs to have seamless transitions and manage content in the event that a user does in fact have to wait. Making design that informs the user of what is happening, such as a loading icon, or using lazy loading to make smaller requests and reduce user wait time.

Lazy loading mean loading some content (often images) asynchronously after the initial viewport of the page is loaded. This means that the page will first render the navigation bar, headers, and images that fill the screen. As the user scrolls down, the additional content begins to fill the page. This is a much more seamless process than waiting for dozens or even hundreds of images to load before anything shows up.

If we return to the restaurant example, a server will often come over when you are first seated and fill your water glasses, bring bread to the table, and bring your drinks before your meal. This is a much better user experience than waiting for half an hour or more to get anything so that your water, bread, appetizer, drink, and meal are all delivered together. That doesn’t make much sense as a restaurant customer, and it doesn’t make much sense for a website user either.