Missed the action at this year's Chrome Dev Summit? Catch up with our playlist on YouTube. Watch now.

Promise.prototype.finally

Promise.prototype.finally is enabled by default in V8 v6.3.165+ and Chrome 63+. It allows registering a callback to be invoked when a promise is settled (i.e. either fulfilled, or rejected).

Imagine you want to fetch some data to show on the page. Oh, and you want to show a loading spinner when the request starts, and hide it when the request completes. When something goes wrong, you show an error message instead.

const fetchAndDisplay = ({ url, element }) => {
  showLoadingSpinner();
  fetch(url)
    .then((response) => response.text())
    .then((text) => {
      element.textContent = text;
      hideLoadingSpinner();
    })
    .catch((error) => {
      element.textContent = error.message;
      hideLoadingSpinner();
    });
};

fetchAndDisplay({
  url: someUrl,
  element: document.querySelector('#output')
});

If the request succeeds, we display the data. If something goes wrong, we display an error message instead.

In either case we need to call hideLoadingSpinner(). Until now, we had no choice but to duplicate this call in both the then() and the catch() block. With Promise.prototype.finally, we can do better:

const fetchAndDisplay = ({ url, element }) => {
  showLoadingSpinner();
  fetch(url)
    .then((response) => response.text())
    .then((text) => {
      element.textContent = text;
    })
    .catch((error) => {
      element.textContent = error.message;
    })
    .finally(() => {
      hideLoadingSpinner();
    });
};

Not only does this reduce code duplication, it also separates the success/error handling phase and the cleanup phase more clearly. Neat!

Currently, the same thing is possible with async/await, and without Promise.prototype.finally:

const fetchAndDisplay = async ({ url, element }) => {
  showLoadingSpinner();
  try {
    const response = await fetch(url);
    const text = await response.text();
    element.textContent = text;
  } catch (error) {
    element.textContent = error.message;
  } finally {
    hideLoadingSpinner();
  }
};

Since async and await are strictly better, my recommendation remains to use them instead of vanilla promises. That said, if you prefer vanilla promises for some reason, Promise.prototype.finally can help make your code simpler and cleaner.