Published at
Updated at
Reading time
3min

If you're a library author, there's always the question of how to implement easy-to-use error handling. First, you want to ensure that your code is bulletproof and does not blowing up in case of an exception, but you also want to guarantee that errors bubble up to the end-user and their error monitoring.

So how do you do this?

Frontend error monitoring with tools like Sentry is usually based on a global error event handler triggered in case of an unhandled exception.

window.onerror = function (message, source, lineno, colno, error) {
  console.log("Global error: " + error.message + ", lineno: " + lineno);
  return true;
};

// Tip: you could also use `addEventListener`
// -> window.addEventListener("error", ...)

function triggerError() {
  throw new Error('Oh no!');
}

triggerError();

// Console output:
// Global error: Oh no!, lineno: 10

This approach works great, but error handling becomes more complicated if you're a few levels deep in your call stack.

Let's look at examples and pretend you're writing library code that accepts event listeners, which you iterate over eventually.

The following code snippets run in an environment that defines a global error event handler such as the one above (window.error = function () {}) and logs uncaught exceptions to the console.

First, iterate over the passed event handlers without any error handling:

// Custom event handlers passed by someone else
const fns = [
  () => { console.log("I'm first!"); },
  () => { throw new Error("Oh no!"); },
  () => { console.log("I'm third!"); },
];

// Iterate over the functions
for (const fn of fns) {
  fn();
}

// Output in the console:
// I'm first!
// Global error: Oh no!, lineno: 10

The global error handler is triggered, and your library users can handle and monitor exceptions. Great! But the thrown exception blows up and stops the loop. The third function is not running.

Let's add exception handling using try/catch:

// Custom event handlers passed by some one else
const fns = [
  () => { console.log("I'm first!"); },
  () => { throw new Error("Oh no!"); },
  () => { console.log("I'm third!"); },
];

// Iterate over the methods
for (const fn of fns) {
  try {
    fn();
  } catch(error) {
    console.error(error);
  }
}

// Output in the console:
// I'm first!
// Error: Oh no!
// I'm third!

The loop succeeds with the added try/catch statement, but the error is no longer bubbling up to the global event handler because it's caught.

How do you pass the exception up the chain then?

There's a hacky way to throw global exceptions... 🙈

for (const fn of fns) {
  try {
    fn();
  } catch (error) {
    // Use setTimeout hack to trigger the global error
    setTimeout(() => {
      throw error;
    }, 0);
  }
}

// Console output:
// I'm first!
// I'm third!
// Global error: Oh no!, lineno: 24

And while using setTimeout works, it's not more than a hack.

How to trigger global error event handlers without hacks

Luckily, there's a new method available to trigger window.onerror or window.addEventListener('error', ...). Say hello to reportError. 👋

The reportError() global method may be used to report errors to the console or global event handlers, emulating an uncaught JavaScript exception.

No matter how deep you're in your application and function calls, reportError allows you to handle exceptions but also trigger the globally defined error handlers.

for (const fn of fns) {
  try {
    fn();
  } catch (error) {
    // add custom error handling but also
    // trigger global error handlers
    reportError(error);
  }
}

// Console output:
// I'm first!
// Global error: Oh no!, lineno: 24
// I'm third!

And the best thing: in terms of cross-browser support, it works across the board already.

MDN Compat Data (source)
Browser support info for reportError
chromechrome_androidedgefirefoxfirefox_androidsafarisafari_iossamsunginternet_androidwebview_android
959595939315.415.417.095

If you're looking for more information on reportError have a look at the following resources:

Was this post helpful?
Yes? Cool! You might want to check out Web Weekly for more web development articles. The last edition went out 5 days ago.
Stefan standing in the park in front of a green background

About Stefan Judis

Frontend nerd with over ten years of experience, freelance dev, "Today I Learned" blogger, conference speaker, and Open Source maintainer.

Related Topics

Related Articles