###Unhandled Rejection Tracking
Several promise libraries such as bluebird and when as well as some native promise implementations offer potentially unhandled rejection tracking. This means that the following:
Promise.reject(new Error("err")); // never attach a `catch`
Does not get silently suppressed being an error but instead gets logged to the console or otherwise treated by the promise library. A common request is a way to handle rejection application wide. NodeJS code often includes several copies of the same promise library or different promise libraries and it is currently difficult to install a global hook on potentially unhandled rejections. For example code using Bluebird for concurrency, akamai for their API, bookshelf as an ORM and request-promise for web requests uses 4 promise libraries already - 3 Bluebirds and a WhenJS. If someone wants to log errors for all tracked unhandled rejections they'd have to add at least four handlers - if they miss any their code won't do what they expect.
This document attempts to offer a standard way for promise libraries and potentially native promises in Node to communicate they have detected an unhandled rejection.
##The hook
In order to handle rejections the following new events will be fired on process
:
###Event 'unhandledRejection':
Emitted whenever a possibly unhandled rejection is detected. This event is emitted with the following arguments:
reason
the rejection reason of the promise susprected in having an unhandled rejectionp
the promise suspected in having an unhandled rejection itself.
process.on('unhandledRejection', function(reason, p){
console.log("Possibly Unhandled Rejection at: Promise ", p, " reason: ", reason);
// application specific logging here
});
###Event 'rejectionHandled':
Emitted whenever a possibly unhandled rejection was mistakenly identified and was eventually handled. For example if the promise library made a mistake considering p
in the following case unhandled:
let p = Promise.reject(new Error("err"));
setTimeout(() => p.catch(function(){}), 86400);
This event is emitted with the following arguments:
p
the promise who was believed to be an unhandled rejection.
This event is useful in order to recover from detection mistakes. While in my experience this rarely happens in practice this additional hook helps in such cases.
##Backwards incompatible changes
None that I am aware of. No one is currently firing events by these names in NodeJS code in GitHub.
##In other languages
Promises as an abstraction are indeed not unique to JavaScript - let's explore what other languages do.
- in C# 4.0 and .NET in general - such a hook exists for Task with an unterminated error handler will crash your application unless you add a
TaskScheduler.UnobservedTaskException
event handler. In 4.5 you can handle errors yourself (like.catch
). - In Scala it used to silent fail (lots of surprised answers in SO - e.g. http://stackoverflow.com/questions/24453080/) but that upset people https://groups.google.com/forum/#!searchin/scala-user/The$20silence$20of$20failed$20futures/scala-user/1X2QISf3604/2dw0XhMh0RUJ , they now intend to support that case.
- Dart futures already do this forwarding rejections to the global error handler.
So overall it seems like other programming environments support a unhandled rejection hander. Note that JavaScript is pretty special in that it is the only case I know of where a lot of different implementations of this abstract concept exist.
##Statistics
These are usage statistics about events current libraries already fire. These usage examples were collected from public GitHub projects using bluebird promises offering (non-global) hooks.
First a summary of bluebird's onPossiblyUnhandledRejection
from GitHub. Large (>1000 stars) libraries typically don't change onPossiblyUnhandledRejection, the largest ORMS: Sequelize, Waterline and Bookshelf do not hook on it however users of those libraries sometimes do. It also very common for people to override it in test code.
Here are libraries and projects using onPossiblyUnhandledRejection
in their code:
- https://github.com/FelisCatus/SwitchyOmega (843 stars, proxy switch, custom logger)
- https://github.com/AppGyver/steroids (237 stars, PhoneGap extension, throw and crash)
- https://github.com/att/rcloud (153 stars, AT&T R script cloud thing, defaults to custom logging)
- https://github.com/simplyianm/githubfollowers/ (101 stars, defaults to log and adds a console log, rather pointless call)
- https://github.com/ripple/gatewayd (89 stars, framework for financial network, custom logging)
- https://github.com/tyabonil/request-promise/ (50 stars, promisified package, actual custom logic)
- https://github.com/mikermcneil/waterline2/ (20 stars, waterline ORM proposed next generation, defaults to suppress behavior)
- https://github.com/PhilWaldmann/openrecord (19 stars, ORM, lets user configure, defaults to log)
- https://github.com/wikimedia/restbase (14 stars, Wikipedia people, log)
- https://github.com/zaggino/brackets-snippets (14 stars, overrides to crash).
- https://github.com/mozilla/fxa-oauth-server (9 stars, by Mozilla, undefined passed to get Q like behavior).
- https://github.com/kenspirit/kanban-cfd (8 stars, Kanban graph visualization, crash)
- https://github.com/mattlewis92/angular-bluebird-promises (6 stars, bluebird for angular, passes noop (this is slighly upsetting :D))
- https://github.com/Evisions/vertebrae (5 stars, Backbone extension, custom logging)
- https://github.com/mzgol/check-dependencies (6 stars, npm dependency check, suppress (TODO above))
- https://github.com/speech/speech.js (3 stars, namecoin domain thing, crash)
- https://github.com/iarna/promisable (3 stars, promises module, references that it acts like
throw e;
not actual bluebird) - https://github.com/maseb/thicket (2 stars, framework, crash)
- https://github.com/sebinsua/neo4j-promised (2 stars, custom logic to overcome heavior change)
- https://github.com/ELLIOTTCABLE/wikipedi.as (2 stars, wikipedia thing, log)
- https://github.com/vkarpov15/wagner-core (1 star, framework, suppress (comment above "swallow bluebird annoying error messages"))
- https://github.com/jdeanwaite/eDVR (1 star, DVR, defaults to log with default to throw commented)
- https://github.com/opinsys/puavo-ticket (1 star, ticket system, custom logging)
- https://github.com/wikimedia/mediawiki-services-restbase (0 stars, mirror of Wikipedia service, logging)
- https://github.com/ZJONSSON/express-clues (1 star, express api wrapper, suppress)
- https://github.com/AaronO/q-bluebird (0 stars, Q API Bluebird wrapper, custom logging)
- https://github.com/mattlewis92/generator-nasty (0 stars, yeoman generator, defaults to log)
- https://github.com/bjoerge/proxyreload (0 stars, proxy, passes null)
- https://github.com/ericnorris/node-cdb (0 stars, cdb implementation, ignores)
- https://github.com/shanesmith/gerrit-cli (0 stars, no diea, throws)
- https://github.com/Bornholm/nap-bootstrap (0 stars, overrides to crash).
- https://github.com/mcirclek/website-client (0 stars, defaults to custom logging)
- https://github.com/chrisdoble/amd-to-closure (0 stars, converts AMD to closure compiler syntax, suppress)
- https://github.com/colonyamerican/cah-logger (0 stars, overrides to crash)
- https://github.com/micirclek/cki-portal (0 stars, site, suppress)
- https://github.com/dinexcode/dnxapp-gatewayd (0 stars, unclear, log)
- https://github.com/srcspider/prototyping (0 stars, seed, defaults to crash)
- https://github.com/martin-liu/m-sails/blob/f02f33a60c424517168b365c0c61da4ad85b8ba6/api/coffee/services/Promise.coffee (0 stars, personal project)
- https://github.com/danprince/cyvasse (Game, 0 stars, undefined passed to get Q like behavior, lots (100) like this using bundler.js)
##Future work
Add a similar solution for promise libraries in browsers, possibly the WhatWG proposal.
##Related work:
Relevant issues:
If requested - a sample implementation guide will be added.
Lie also added it.