DEV Community

Sal Rahman
Sal Rahman

Posted on

Promises and async functions

With JavaScript, some operations are asynchronous, and many of these asynchronous operations are signalled via promises.

For instance, fetching data from an API is an asynchronous operation; you need to wait until data from the API has been fully downloaded. So, invoking fetch doesn't give you the data. Instead, it gives you a promise, from which you will need to have another function be called to receive said value, as the first parameter of that function.

With a promise, to get the result the instance that is available, you invoke the then method, and pass in said function as the first parameter.

Here's an example:

const fetchPromise = fetch('http://example.com/some-api/');


fetchPromise.then(response => {
  // Do something with `response` here.
  //
  // Note: the `response` object doesn't actually contain the data. You will
  // need to invoke either `response.text()` to extract the data into a string
  // object, or `response.json()` to extract the data into an object, if the
  // incoming data is valid JSON.
});
Enter fullscreen mode Exit fullscreen mode

With fetch, we have access to a response object.

But from the response object, we will need to extract the value. And that is done via invoking either response.text(), or response.json(). Both of these methods will yield a promise!

So here's what the above code would look like if we wanted to extract the textual value of the response.

const fetchPromise = fetch('https://example.com/some-api/');


fetchPromise.then(response => {
  const textPromise = response.text()

  textPromise.then(value => {
    // The paramter `value` will be the extracted string.
  });
});
Enter fullscreen mode Exit fullscreen mode

But it gets better.

You know how in arrays, there's a flatMap function, and it can accept as return value another array?

The then method in promises acts like flatMap, where you can return another promise, from the callback function in then.

So, to extract the text value, you can invoke the above function like so:

const fetchPromise = fetch('https://example.com/some-api/');


fetchPromise.then(response => {
  const textPromise = response.text();

  return textPromise;
});
Enter fullscreen mode Exit fullscreen mode

Above, we merely returned the promise. But how do we extract the value?

Before going into that, also note this important fact: the then method will always return a promise!

And that promise will—at a high level—be exactly equal to what is returned by the callback in the then.

So, to extract the text, the above code would look like so:

const fetchPromise = fetch('https://example.com/some-api/');


fetchPromise.then(response => {
  const textPromise = response.text();

  return textPromise;
}).then(text => {
  // The value will be in `text`
});
Enter fullscreen mode Exit fullscreen mode

Since we've established where promises typically come from, let's shorten the above code.

fetch('https://example.com/some-api/')
  .then(response => response.text())
  .then(text => {
    // The value will be in `text`
  });
Enter fullscreen mode Exit fullscreen mode

Let's say the above API yields a string, and we can use that string to invoke another API call. Let's do that.

We can have numerous ways to do that.

We can nest the invocations.

fetch('https://example.com/some-api/')
  .then(response => {
    return response.text()
      .then(text => {
        return fetch(`https://example.com/some-api/{text}`)
          .then(response => response.text());
      });
  })
  .then(text => {
    // The value will be in `text`
  });
Enter fullscreen mode Exit fullscreen mode

We can nest some of the invocations. Perhaps to group the "response-to-text extraction" logic.

fetch('https://example.com/some-api/')
  .then(response => response.text())
  .then(text => {
    return fetch(`https://example.com/some-api/${text}`)
      .then(response => response.text());
  })
  .then(text => {
    // The value will be in `text`
  });
Enter fullscreen mode Exit fullscreen mode

Or have everything be sequential.

fetch('https://example.com/some-api')
  .then(response => response.text())
  .then(text => {
    return fetch(`https://example.com/some-api/${text}`)
  })
  .then(response => response.text())
  .then(text => {
    // The value will be in `text`
  });
Enter fullscreen mode Exit fullscreen mode

Async functions

OK, the above invocation of then is cumbersome, in many situations. So, a solution to limit the number of then invocations would be to use an async function.

An async function looks like this:

async function something() {
  return 42;
}
Enter fullscreen mode Exit fullscreen mode

An async function doesn't merely return anything. It only returns a promise!

So, invoking something() will yield a promise.

something()
  .then((value) => {
    console.log(value); // should print 42
  });
Enter fullscreen mode Exit fullscreen mode

It gets even better. An async function allows you to resolve promises without invoking then. You would use the await keyword for that.

So, for instance, if fetch were to be invoked inside an async function, it would look like so:

async function doSomething() {
  const response = await fetch('https://example.com/some-api');
  return response.text();
}
Enter fullscreen mode Exit fullscreen mode

Since async functions return a promise, we can have the above fetch invocations simplified to this:

fetch('https://example.com/some-api')
  .then(async response => {
    const text = await response.text();
    const response2 =
      await fetch(`https://example.com/some-api/${text}`);
    return response2.text();
  });
Enter fullscreen mode Exit fullscreen mode

I don't know about you, but I'm not a fan of superfluous response variables. A solution is to use then to avoid creating those variables.

fetch('https://example.com/some-api')
  .then(async response => {
    const text = await response.text();
    return fetch(`https://example.com/some-api/${text}`)
      .then(response => response.text());
  });
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
nothingbutcoding profile image
Ash

Great article, thanks