Ten Minutes on Asynchronous Callbacks in JavaScript

Judith Urban
Feb 9, 2019 · 9 min read
Image for post
Image for post

Synchronous Functions

So here’s my claim: any time we write our program’s code, we’re actually architects, sketching out blueprints for everything that’s supposed to happen on runtime. A program may run so fast that it seems like lots of things happened at the same time. But if we slow its execution down, all we really see is the engine executing one statement after another. The engine just sticks to its blueprint very diligently. Line by line, it will run through the source code. Everything that needs to be taken care of lands on the engine’s “to-do“ stack. I’d like to illustrate this with this blueprint for a program that uses functions to log out strings.

function third() {

function second() {

// Prints:// "first"
// "second"
// "third"
Image for post
Image for post
At this exact point in time, our third function pops up on the call stack. The engine is tending to that one right away.

Synchronous Callbacks/Executors

You probably already know that functions are first-class citizens, so they can be arguments for another function. The fact that we can pass functions around is an essential feature of JavaScript. A typical scenario might look like this:

const array = [1, 2, 3, 4];// pass a synchronous function to the map() function
const double = array.map(x => x * 2);
console.log(double);// [2, 4, 6, 8]

Asynchronous Functions

Sometimes, we need do instruct the engine to start a process that is time-consuming. Whenever we ask it to read files in order to use their data later on, for example. Or when we ask a third party if they could process an image or look up some information. If our program sends out these kinds of requests, we don’t know how long it will take until the the response is available.

const content = requestSync("https://www.sloth.com");console.log(content);// „This is a website about me, sloth“
Image for post
Image for post
A frozen program.
const content = request("https://www.sloth.com");console.log(content);// Undefined

Asynchronous Callbacks

Let’s take another look behind the scenes, as there’s something we have missed until now. Our runtime environment has its own nice built-in conveyor belt: The task queue. It is built for cases where external processes that we can’t control are involved. Third party code can put chunks of code on our task queue. Why is that useful? Because whenever we give a third party instructions for something asynchronous to happen, we can send along an “after-that-chunk-of-code”. We do that by sending along a callback function whenever we call asynchronous third-party-functions.

request("https://www.sloth.com", content => console.log(content));// "This is the website about me, sloth"
Image for post
Image for post
A new callback has arrived in the task queue. It will be dealt with when the call stack is empty.


It might not be so obvious in our everyday life, but the big difference between synchronous executors and asynchronous callbacks is all about what actually happens on runtime. Synchronous callbacks, or executor functions, pop up on the call stack to be taken care of right away.

Image for post
Image for post


We build cutting-edge software based on open web standards.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store