Last Resort aims to help your application catch unhandled exceptions and unhandled rejected promises in the browser.
Last Resort is tested using BrowserStack. BrowserStack provides this service for free under their program for supporting open-source software.
Last Resort is supported on:
-
Desktop: Chrome, Firefox, Edge, IE11, IE10, IE9, Opera, and Safari (on El Capitan, Yosemite, Mavericks).
-
Mobile: iOS 9.3 down to 6. Android 4.4. Presumably later versions of Android are also fine, although we've not been able to test them.
We test against the latest versions offered by the vendors of these browsers on their respective platforms.
Borderline cases:
- The suite runs on IE9 but it does not perform a thorough check of the values obtained from the events generated by the browser. There are a few quirks there.
If you see a platform you think should be tested but isn't, then chances are
that Browser Stack is not providing support for it, or is not providing stable
support for it. You should inspect the karma.conf.js
file to see platforms
that have been commented out. If you can get these platforms to work, you are
welcome to put in a pull request.
Not supported, and unlikely to be supported, ever:
-
Android versions less than 4.4.
-
iOS versions less than 6.
IMPORTANT IMPORTANT IMPORTANT: Last Resort depends on
<global>.addEventListener("unhandledrejection", ...)
being supported in your
environment in order to trap unhandled rejections. If your platform does not
support this, then Last Resort will not be able to trap unhandled
rejections. This
page
suggests that only Chrome 49 and over supports it natively. On browsers that do
not support it natively, you must use a promise library that provides support
for unhandledrejection
events. Bluebird is one such
library. It is actually the library used for testing Last Resort's handling of
unhandled rejections.
Note on using Last Resort in workers. Last Resort, when used with Bluebird,
is able to trap unhandledrejection in workers. However, it has to use
`self.onunhandledrejectionrather than
self.addEventListener``. This is
because a lot of platforms (including all Microsoft browsers) do not
actually allow using ``self.addEventListener`` to listen for custom
exceptions. See this
comment
for details.
Last Resort can be loaded in the following ways:
-
In a
script
element. In this case it will be available asLastResort
on thewindow
object in which it has been loaded. -
As an AMD module. It will export the same thing as what
LastResort
contains in the scenario above. You may name the module whatever you want, so long as you define a path for it in yourpaths
configuration. -
As a CommonJS module. It will export the same as the thing as the earlier options.
-
In a
script
element and then as an AMD or CommonJS module. This is not a common way to load scripts but Last Resort supports it. When it is loaded byscript
, it createsLastResort
like in the first scenario above. When it is then loaded again as a module, it checks whetherLastResort
already exists in the global space, and returns that if it exists, rather than create a new module. IfLastResort
does not exist, then it will initialize itself from scratch. Note that, when loaded first throughscript
and then as a module,LastResort
will still be present in the global space if it had only been loaded throughscript
.
Why this 4th method? This is supported in order to allow loading Last Resort as
early as possible, while still allowing it to be referenced as a module, and not
forcing modules that need to use it to refer to a global. Ideally, Last Resort
should be loaded before any other code so that it can report errors as early as
possible. In one project in which it is used, it is loaded with a script
element, then we register an absolutely bare error handler that uses alert
to
report to the user, then more substantial scripts are loaded, including an AMD
module that provides a Bootstrap-based dialog in case of error. This module
loads Last Resort as an AMD module and replaces the initial barebones error
handler with its more sophisticated one.
Last Resort listens to these two events:
-
error
, which is emitted when a thrown exception is not caught by any code. -
unhandledrejection
, which is emitted when a rejected promise is not handled by any code.
In the following when we say "the events" we are referring to these two events.
Last Resort exports these items:
-
install(context, options)
installs Last Resort to intercept the events oncontext
. The parametercontext
is oftenwindow
but could be the global context of a worker too (self
). It returns an instance ofOnError
this instance is what is now handling the events.The
options
parameter is a plain object holding possible options:-
force
when set totrue
will force the installation of Last Resort on thecontext
even if it was already installed previously. Otherwise an exception is raised. Ifforce
is used and Last Resort was previously installed, the previous installation will be automatically uninstalled. -
noUnhandledRejection
when set totrue
will NOT install a handler to catch unhandled rejections.
-
-
The
OnError
class, which has the following methods:-
register(fn)
registers the functionfn
to handle the events.fn
will be called with the event objects generated for theerror
andunhandledrejection
events. It should be prepared to examine the event to determine what happened exactly. Untilregister(fn)
is called, theOnError
instance does not do anything when an event is emitted. -
uninstall()
uninstalls theOnError
-
-
isInstalled(context)
returnstrue
ifinstall(context)
was ever called.false
otherwise. -
wasTriggered()
returnstrue
if any event was raised in any context.
By default, the registered function is called both when an uncaught exception happens and when an unhandled Promise rejection happens. So it has to be ready to examine its arguments and determine how to process that information depending on the types and number of the arguments passed.
When the registered function is called, Last Resort calls uninstall
on the
OnError` object that bears the registered function. This prevents infinite recursion if it happens that the registered function causes new errors. It is up to the developer of the registered function to ensure that this function does not screw things up while it is running. It *could* call
LastResort.install``
anew if desired and register its own specialized handler. Or it could do its own
error handling.
Some old browsers bypass onerror
handlers attached to iframe
elements
and instead go straight for the one on the root window. It is up to your
application to decide whether it needs to support these old browsers and use
Last Resort appropriately (e.g. detect the issue and install on the root window
instead of installing on an iframe
).
Here is a super simple example:
import * as lr from "last-resort";
const onerror: lr.OnError = lr.install(window);
function handle(ev: Event): void {
// tslint:disable-next-line:no-console
console.log(ev);
}
onerror.register(handle);
This example just dumps the error to the console.
If you produce a pull request run gulp test
first to make sure it is
clean. If you add features, do add tests for them.