Promise Rejection Events Sample

Available in Chrome 49+ | View on GitHub | Browse Samples

Background

Two new global events, unhandledrejection and rejectionhandled, can be used to keep track of Promise rejections, including whether those rejections are handled after the fact.

The unhandledrejection event is the promise-equivalent of the global error event, which is fired for uncaught exceptions. Because a rejected promise could be handled after the fact, by attaching catch(onRejected) or then(onFulfilled, onRejected) to it, the additional rejectionhandled event is needed to indicate that a promise which was previously rejected should no longer be considered unhandled.

These two events can be used, for example, to keep track of unhandled rejections as part of an error-reporting framework.

Live Output

The buttons below can be used to create new rejected promises that are either eventually handled, or just left unhandled. The corresponding unhandledrejection events (and rejectionhandled if the promise is eventually handled) are logged.

This sample also shows how a web app might keep a running tally of all unhandled rejections in a Map object. This could come in handy when working with a centralized error reporting infrastructure, where you want to log all rejected promises that have gone unhandled up to that point.


JavaScript Snippet

// Use a Map to keep track of rejected promises and their corresponding reason.
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
var unhandledRejections = new Map();

window.addEventListener('unhandledrejection', event => {
  ChromeSamples.log('unhandledrejection fired: ' + event.reason);
  // Keep track of rejected promises by adding them to the Map.
  unhandledRejections.set(event.promise, event.reason);
});

window.addEventListener('rejectionhandled', event => {
  ChromeSamples.log('rejectionhandled fired: ' + event.reason);
  // If a previously rejected promise is handled, remove it from the Map.
  unhandledRejections.delete(event.promise);
});

function generateRejectedPromise(isEventuallyHandled) {
  // Create a promise which immediately rejects with a given reason.
  var rejectedPromise = Promise.reject('Error at ' +
    new Date().toLocaleTimeString());

  if (isEventuallyHandled) {
    // We need to handle the rejection "after the fact" in order to trigger a
    // unhandledrejection followed by rejectionhandled. Here we simulate that
    // via a setTimeout(), but in a real-world system this might take place due
    // to, e.g., fetch()ing resources at startup and then handling any rejected
    // requests at some point later on.
    setTimeout(() => {
      // We need to provide an actual function to .catch() or else the promise
      // won't be considered handled.
      rejectedPromise.catch(() => {});
    }, 1);
  }
}

function reportUnhandledRejections() {
  ChromeSamples.log('[Unhandled Rejections]');
  for (var reason of unhandledRejections.values()) {
    ChromeSamples.log(' ', reason);
  }
  unhandledRejections.clear();
}

document.querySelector('#handled').addEventListener('click', () => {
  generateRejectedPromise(true);
});

document.querySelector('#unhandled').addEventListener('click', () => {
  generateRejectedPromise(false);
});

document.querySelector('#report').addEventListener('click', () => {
  reportUnhandledRejections();
});