TypeOfNaN

How to return a response from an asynchronous call in JavaScript

Nick Scialli
March 18, 2023

Asynchronous JavaScript is one of the trickier aspects of the language. In this post, we’ll see how to handle the result of an asynchronous call.

Creating an example

Let’s create a contrived example. We’ll make a delayedRandom function that returns a random number after a small delay. Here’s how that might look:

function delayedRandom(ms) {
  setTimeout(() => {
    return Math.random();
  }, ms);
}

delayedRandom(100);

// How do we get the result?

In this function, we provide ms, which stands for milliseconds. We use the setTimeout function that will create a random number after the provided number of milliseconds.

When we call delayedRandom; however, we realize we don’t know how to get the generated random number.

Callback functions

The first solution we’ll provide is the callback function. JavaScript has “first class functions,” which means we’re allowed to pass functions are arguments to other functions. That’s example what a callback function is— it’s a function we pass to another function. Inside the other function, we can exection the callback function whenever we want!

Let’s create a new function that we want to execute once the random number is generated:

function callback(number) {
  console.log('The random number is: ' + number);
}

Now, we can pass this callback function to delayedRandom and call it when the number is generated:

function delayedRandom(ms, cb) {
  setTimeout(() => {
    cb(Math.random());
  }, ms);
}

function callback(number) {
  console.log('The random number is: ' + number);
}

delayedRandom(100, callback);

If we execute this code, we’ll see the following logged to the console (with a different random number each time you run it):

The random number is: 0.6947937030841276

If this is confusing, don’t worry—you’re not alone in thinking so! It’s important to look at the exact arguments passed to delayedRandom (100 and callback) and see how they’re used in the function as ms and cb, respectively.

Promises

Callbacks can be a bit challenging to follow and can get messy when combining multiple callbacks together (this is generally referred to as callback hell). Promises are a built-in JavaScript object that can provide a friendlier way to handle asynchronous code.

Promises represent the eventual completion of an asynchronous task. Promises can either resolve, meaning the operation was successful, or reject, meaning there was an error.

Let’s convert our example above to use promises and then explore what’s going on:

function delayedRandom(ms) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(Math.random());
    }, ms);
  });
}

delayedRandom(100).then((number) => {
  console.log('The random number is: ' + number);
});

Here we return a new Promise object from delayedRandom. When instantiating the Promise object, we pass a function that currently has one argument—resolve. Calling resolve will signal that the promise has been fulfilled. We see that the callback is moved inside a then call chained on to delayedRandom, which is what gets called when the promise is fulfilled.

I had previously mentioned that a promise can be rejected, so let’s explore that case. We can pretend that, if our random number is less than 0.1, the promise will reject.

function delayedRandom(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const num = Math.random();
      if (num < 0.1) {
        reject(num);
      } else {
        resolve(num);
      }
    }, ms);
  });
}

delayedRandom(100)
  .then((number) => {
    console.log('The random number is: ' + number);
  })
  .catch((number) => {
    console.error('Oh no, the number was ' + number);
  });

We can see that errors hare handled with chaining catch on to our returned promise object.

Conclusion

Callbacks and promises are a couple different ways we can handle asynchronous code in JavaScript. While this is a paradigm shift from many other languages, if you continue practicing you’ll soon find yourself doing really great things with async code.

If you'd like to support this blog by buying me a coffee I'd really appreciate it!

Nick Scialli

Nick Scialli is a senior UI engineer at Microsoft.

© 2024 Nick Scialli