Create a Global Promise Rejection Handler
Nov 4, 2020 • 4 Minute Read
Introduction
Promises changed the game when it came to modeling asynchronous tasks within a JavaScript program. They can allow you to refactor huge swathes of code that are mired inside of "callback hell," a term coined to describe code bases that relied upon callbacks in order to model asynchronous actions. Callbacks allowed for a lot of nested code, and they weren't that great when it came to handling errors either. The error-handling convention with callbacks was that you would ensure that the first parameter within your callback function would house a potential error. You would then have to check if that parameter existed in order to ensure that you handled it correctly. Promises built on this with the catch method for handling errors. The catch method ensures that any upstream errors will be handled.
This guide will demonstrate how to take this a step further and add a global promise rejection handler so that you can capture every single instance of a rejected promise at runtime within your program.
Let's get started.
The 'onunhandledrejection' Event Handler
Circa 2016, browsers provided developers a new, globally available property that an event handler could be assigned to. The onunhandledrejection event occurs every time that a promise rejection is not handled. In the below code, you can see an example of a rejected promise that goes unhandled.
const myPromise = new Promise((resolve, reject) => {
reject('Ouch');
});
myPromise.then(() => {
console.log('Hello world');
});
// An Unhandled Promise Rejection Error will occur ^^
The simple code example above creates a promise that will always be rejected. When an attempt to resolve the promise is made, an error will occur because there was no catch method used to handle any potential errors. This caused an uncaught error to bubble up within the app!
onunhandledrejection event handler to the rescue! With the onunhandledrejection event handler, you can provide a global failsafe that will catch all rejected promises that go unhandled.
The code below has been updated to provide our global promise rejection handler.
// This function handles any unhandled promise rejections
const globalPromiseRejectionHandler = (event) => {
console.log('Unhandled promise rejection reason: ', event.reason);
}
// Here we assign our handler to the corresponding global, window property
window.onunhandledrejection = globalPromiseRejectionHandler;
const myPromise = new Promise((resolve, reject) => {
reject('Ouch');
});
myPromise.then(() => {
console.log('Hello world');
});
And voila! No more uncaught errors. When you use the code directly above, you should see a message in the console that reads, Unhandled promise rejection reason: Ouch. Simply by creating a function event handler and assigning it to the onunhandledrejection property on the globally available window object, you have created a safe means of catching any unhandled promise rejections within your app. This technique is very useful for logging purposes but also allows you a safe fallback any time that a catch method is not used when it should be.
Note: Where possible, attempt to always use the catch method in order to handle promise rejections close to where they occur.
Conclusion
Promises are a huge improvement to the JavaScript ecosystem both in terms of modeling asynchronous actions and when it comes to handling errors.
By using the globally available onunhandledrejection property, you can easily create an event handler that will catch any errors at runtime. This can prevent a lot of undefined behavior from happening in your app at runtime. And that's always a good thing for the user!
For more information about the onunhandledrejection property, check out the documentation.