What is the difference between callback functions, promises, and async/await in Javascript?
Tags: javascript, snippets
Published: 2021-03-15 17:09:27

The TL:DR - version:

1.  [Callbacks](#callback) are functions passed as arguments into other functions to make sure mandatory variables are available within the callback-function's scope
2.  [Promises](#promise) are placeholder objects for data that's available in the future. As soon as their state changes from `pending` to `resolved`, `.then()` method can be called to make the data available for subsequent operations.
3.  [Async / await](#async) is syntactic sugar over promises. Instead of using `.then()`, you can assign data that's `resolved` by a promise to a variable available within an `async function()` scope.

A big part of what Javascript does best has to do with sending and processing data over the internet. There's a good chance that you'll stumble across [AJAX](https://developer.mozilla.org/en-US/docs/Web/Guide/AJAX), specifically in terms of [making API requests](https://q-bit.me/ghost/#/editor/post/5fd3e59d71ccad9487555e60) quite early in your web dev journey.

You'll have to consider asynchronous code execution whenever you are:

1.  Calling data from remote resources
2.  Waiting for other processes to delivery computed informaiton back into your application's scope

While you might encounter the first more often in your frontend and the second in your node.js backend, in both cases **callback functions** and **promises** are bound to come into play.

> In a (really tiny) nutshell, each of these variants make sure that remotely called resources - or computed variables are ready for usage when they're needed.

Asynchronous programming
------------------------

To understand asynchronous code execution, it helps to put the concept into words and compare it with its synchronous counterpart.

> If you're aware of the basics, feel free to skip to [Callback functions](#callback).

Assume you have three lines of code that do the following:

1.  A variable declaration that assigns the number 5 to a variable named `number`.
2.  A function that takes in variable `number` and sends it to a `server` (url) via ajax.
3.  An alert function that passes back the result from (2) to a user.

Then, consider the follwowing JS pseudocode:

```javascript
/*(1)*/ const number = 5; 
/*(2)*/ const serverResponse = getServerData(url, number)
/*(3)*/ alert(serverResponse)
```

If you were using a synchronously executed language, this construct may work just fine. Take a number, evaluate it serverside and alert the result.

A Javascript engine, however, will not wait for (2) to evaluate; if `serverResponse` is not available right away, it will call the alert and you will see something like this:

![](https://q-bit.me/content/images/2021/02/01_asynchronous_javascript.jpg)

What else could happen is your console throwing an error, stating that `serverResponse` is not defined. Both exceptions are symptomes of the same cause.

> The reason is the order in which the two functions get called. Javascripts [event loop](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop) creates a queue of tasks being worked on in a given order. However, it does't wait for either of those tasks to finish, but executes the next one in line right away without waiting for an evaluation.

To prevent this from happening, we have to make sure that variables are available for consumption before they're assigned or used for other functions. At that point, callbacks and promises come into play.

Callback functions
------------------

A callback is a function (let's call ours `bar`) that is called right before another function finishes (function `foo`).

> It's **Call**\[ed in the\] **back** \[of another\] **function**

For that to happen, `bar` must be passed into `foo` as an argument, so that the variables that have been evaluated in `foo` are available in the function scope of `bar`.

So far so good? Let's take a look at the following example:

```javascript
// First, define bar, our callback function.
const bar = (fooNumber) => {
  return fooNumber + 5;
};

// Then, define the function that delivers variables for bar
const foo = (callback) => {
  const myNumber = 10;

  // 'callback' is the placeholder for the actual function
  callback(myNumber);
};

// Finally, execute foo and pass fooNumber into bar
foo((fooNumber) => console.log(bar(fooNumber))); // expected result: 15
```

It might look quite alien at first sight, so we'll replicate this behavior with a synchronous example that works just the same:

```javascript
const bar = (fooNumber) => {
  return fooNumber + 5;
}

const foo = () => {
  const myNumber = 10;
  return myNumber;
}

console.log(bar(foo())) // expected result: 15
```

Both of the above functions return the exact same result, but differ in how they get there.

*   The first function evaluates `foo` and passes its result on the next function, making sure it is available for `bar`
*   The second function evaluates inside - out. It executes `foo`, followed right away by `bar`, using `foo's` result as an argument.

And here comes the trick - What would happen if, in scenario 2, `foo` was not yet evaluated into 10, but takes a moment (half a second) to get that number from elsewhere?

```javascript
const bar = (fooNumber) => {
  return fooNumber + 5;
}

const foo = () => {
  setTimeout(() => {
    const myNumber = 10;
    return myNumber;
  }, 500)
}

console.log(bar(foo())) // expected result: ?
```

The result will be `NaN`, as `foo`, at the moment its value is read within `bar`, is `undefined`.

Let's now put this timeout into the callback example:

```javascript
const bar = (fooNumber) => {
  return fooNumber + 5;
};

// Then, pass it into foo as an argument
const foo = (callback) => {
  setTimeout(() => {
  const myNumber = 10;
  callback(myNumber);
  }, 500)
};

foo((fooNumber) => console.log(bar(fooNumber))); // expected result: ?
```

That looks much better, we're back to 15.

> **As a callback is executed as a part of another function, these two are considered to be the same task (message) in the event loop's sequence flow.**

This is not the case in the second, non-callback example, therefor `foo`'s evaluation is undefined and the result is `NaN`.

Promises
--------

You might have noticed the type of the object that was alerted in the first example above. It was not the expected variable from `serverResponse`, but neither was it undefined.

What you have seen was a **placeholder** for a variable that will be there at some point in the future. Imagine it like the small buzzer you're handed in a restaurant while you're waiting for your food to be delivered. When handed to you, the exact moment of when your dish arrives is unknown, but it will do at some point. You will be notified by the `state` of the buzzer (changing from inactive to buzzing) as soon as the moment comes.

![](https://q-bit.me/content/images/2021/02/buzzer-628927_640-2.jpg)

A buzzer is a literal promise for your food to arrive at some point#

As soon as the buzzer goes off, the promise made to you is `resolved`, and you can go and claim your food. Only `then`, it is available to you for eating.

> [window.fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) and [Javascript service workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) are two innovative browser technologies that heavily rely on promises. Especially service workers run in a purely asynchronous context, making data availability the most crucial prerequisite for proper results.

Let's try and replicate this example in code features:

*   When the order is placed, the exact moment of food availability is unknown, but always takes between 5 and 20 minutes (seconds in the code).
*   A placeholder (Promise - object) is handed out to the calling function.
*   It resolves into the amount of time that has passed since order placement, but only once the food is ready.

And now to the related code that simulates the waiting time:

```javascript
// Imagine to be at a restaurant and place an order
const orderFood = () => {

 // A buzzer will be handled to you
 return new Promise((resolve, reject) => {
 
  // Cooking time could be anything between 5 and 20 seconds
  const cookingTime = 5000 + Math.random() * 15000;

  // The food will be prepared in the given time
  setTimeout(() => {
   const foodReady = true;

   // If the food is ready after the cooking time,
   // pass the information on to the buzzer. Also,
   // pass on the cooking time in seconds
   if (foodReady) {
    const time = (cookingTime / 1000).toFixed();
    resolve(time);

    // If it is not ready for some reason, throw an exception which
    // you can later catch when calling the function
   } else {
    const reason = 'Your food could not be prepared ...';
    reject(reason);
   }
  }, cookingTime);
 });
};

// Call the initial function. Wait for it to resolve
orderFood()

 // The variable in the .then method is what you have passed
 // into the resolve function within the promise
 .then((time) => {
  console.log(`BZZZZZ BZZZZZ - Your food is ready.`);
  console.log(`Your waiting time was ${time} seconds`);
 })

 // Catch the reason for the promise rejection
 .catch((reason) => {
  console.log(reason);
 })

 // Perform an operation after any type of outcome
 .finally(() => {
  return 'Handing buzzer back to restaurant staff'
 });
```

Note that there's more to promises, such as the [Promise.all()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) and [Promise.any()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/any) methods, which give you an even better control of asynchronous code processing. They're out of scope for this article, but worth mentioning at this point.

Async / await
-------------

... is actually syntactic sugar over promises and not a separate technique. Instead of returning a single placeholder per function, you can declare the same with the help of of an`async` function and use the keyword `await` inside that function's scope whenever trying to assign a variable with a value that's not available yet. While the functionality is the same, asynchronous functions look more like that type of coding you're already familiar with.

Let's try and rephrase the above function call of `orderFood()` in async style.

```javascript
// Promise style
orderFood()
 .then((time) => {
  console.log(`BZZZZZ BZZZZZ - Your food is ready.`);
  console.log(`Your waiting time was ${time} seconds`);
 })

// async/await style
(async () => {
 // Instead of chaining .then() methods, you can use the await keyword
 const time = await orderFood();
 console.log(`BZZZZZ BZZZZZ - Your food is ready.`);
 console.log(`Your waiting time was ${time} seconds`);
})();
```

When dealing with multiple promises, instead of chaining `.then()` - methods over several promises, you could keep assigning variables as you did before, within a single function's scope. Writing async/await functions might also make longer code files more readable and prevents you from ending up in a `.then()` - type of [callback hell](https://www.freecodecamp.org/news/how-to-deal-with-nested-callbacks-and-avoid-callback-hell-1bc8dc4a2012/).

> A note: Try and avoid using promises together with async / await in the same file. Choose the one you prefer and stick with it - your team will thank you.

Bonus: A peak into advanced promises
------------------------------------

Okay, before I finish, let me give you an idea of the previous teaser.

Since their introduction, promises became a core part of asynchronous Javascript programming. With this transition came many useful features - including concurrent resolving of several promises at once.

The method in question is `Promise.all()`. It makes sure that all promises you pass into it are resolved before moving ahead in the code chain. This comes in especially handy if you use two or more remote resources that have dependencies on one another.

I will not go into detail here - perhaps in a later article - but the commented code below should give you an idea of how `Promise.all()` works. If you have not encountered `fetch()` yet, you can find a brief introduction [over at MDN](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).

For the example below, I am using [JSONPlaceholder](https://jsonplaceholder.typicode.com/), a fake API that delivers mock data in json format.

```sh
# Install npm package for serverside fetch
$ npm i node-fetch
```

```javascript
// Import the fetch module for serverside fetch execution
const fetch = require('node-fetch');

(async () => {
  // Assign one promise (fetch) to each variable
  const users = fetch('https://jsonplaceholder.typicode.com/users');
  const posts = fetch('https://jsonplaceholder.typicode.com/posts');
  const albums = fetch('https://jsonplaceholder.typicode.com/albums');

  // Wait for all three promises to resolve
  const responses = await Promise.all([users, posts, albums]);

  // Transform the promise body into json
  const data = await Promise.all(responses.map((el) => el.json()));
  console.log(data);

  // To each user, assign the corresponding post and albums
  const userData = data[0].map((user) => {
    user.posts = data[1].filter((post) => post.userId === user.id);
    user.albums = data[2].filter((album) => album.userId === user.id);
    return user;
  });

  // Voilá - the users received their matching albums and posts
  console.log(userData);
})();
```
> This post was originally published at https://blog.q-bit.me/what-is-the-difference-between-callback-promises-and-async-await/
Thank you for reading. If you enjoyed this article, let's stay in touch on Twitter 🐤 [@qbitme](https://twitter.com/qbitme)