deno: onunhandledrejection for globalThis?
I was looking for an equivalent to the Node.js “unhandledrejection” event in Deno, but it appears there isn’t one on globalThis.
There does appear to be an onunhandledrejection event handler defined on workers, though.
Is there an equivalent event outside of workers, and if not, could there be? 😄
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 17
- Comments: 23 (9 by maintainers)
Commits related to this issue
- feat(core): intercept unhandled promise rejections Provide a programmatic means of intercepting rejected promises without a .catch() handler. Needed for Node compat mode. Also do a first pass at unc... — committed to bnoordhuis/deno by bnoordhuis 3 years ago
- feat(core): intercept unhandled promise rejections Provide a programmatic means of intercepting rejected promises without a .catch() handler. Needed for Node compat mode. Also do a first pass at unc... — committed to bnoordhuis/deno by bnoordhuis 3 years ago
- feat(core): intercept unhandled promise rejections (#12910) Provide a programmatic means of intercepting rejected promises without a .catch() handler. Needed for Node compat mode. Also do a first... — committed to denoland/deno by bnoordhuis 3 years ago
Deno will always die on unhandled exceptions / rejections.I’d like to request the ability to change this for uncaught promises.
I know a lot of people just say hey if this happens it is your fault. Technically your right. But this is reality. And people make mistakes. Production crashing because of a mistake is a serious issue. Especially when it is so easily preventable. But the problem really gets amplified when even if you are perfect, and your code catches everything and everyone, you can still crash if one of your deps forgets. So you can not just rely on yourself being perfect but the entirety of the world needing to be perfect. This in my opinion makes Deno very hard to want to use in production. Processes crashing are not an acceptable solution imo.
Let’s take a look at a simple example. Please bare with me here.
In this example, what happens is the process crashes. There is no way for me to catch this error. It’s literally impossible. Since
cis not returning a promise i can not catch it or try/catch + await it either. Because the developer of that third party dep forgot to catch something, the process crashed. Applying this to a real project, this could be god knows how many deps deep. So the argument that you have to check every line of your deps code isn’t really possible realistically. Nor is having to check every single commit of every single dependecy. This just adds on to more reason of why Deno is impossible to use in production.If this was nested some 10-15 levels deep, we would never be able to debug this and even attempt a fix.
The other day, this actually caused my own project to fail.
When I saw this, I was confused out of my mind. What on earth is core.js? I don’t have any file called core.js. I don’t know of any dep that has this and it sounds like it could literally come from any dep or even Deno itself or even V8. I just don’t know. Luckily, my project has very little deps so I went through debugging spendings hours to figure this out. Just to find out there was a bug in listenDatagram(which isn’t an issue, it’s still unstable). The issue is that this behavior can cause a process in production to crash unknowingly. It becomes a nightmare to debug and solve.
This is all to say that Deno in production is really hard to vouch for. Realistically Deno is only usable in production if you do not use any deps for your projects.
Now for another point of view. Deno is realllllllly hard to use for beginners. A lot of users wanting to use my library are first time developers. This means they are just getting into their very first coding project EVER! I have made it as easy as possible for users like this to be able to get started. They can use the strictest TS settings without even needing to know a thing about typing or casting or anything. However, this 1 issue of them forgetting .catch is a serious issue.
Time and time again I have users reporting their processes crashing because they simply forgot to catch something. Not have a training wheel so to speak makes this really difficult. A simple handler to prevent crashes could be soooo useful because I could have this internally built for them so it alerts them of where they are forgetting to catch so that they can fix their code and prevent their processes from crashing.
@Skillz4Killz This will not be implemented to prevent crashes. If implemented it will only be able to be used to perform synchronous cleanup of allocated resources (e.g. file descriptors, handles, etc). It will not be usable to prevent shutdown on an unhandled rejection. Instead you should catch promise rejections correctly. Deno will always die on unhandled exceptions / rejections.
My use case is a testing tool, which is supposed to detect and report any, even unexpected exceptions, raised by the test code. This is not possible if the process synchronously dies on any exception.
I came across this issue because I had a similar cryptic error. Here are my two cents on this.
I myself forgot to await a call to
Deno.Conn.write()and then calledDeno.Conn.close()which of course crashed the process.this was my error:
How the hell am I supposed to debug that… I’m honestly surprised I was able to find the source. If that was in a library, quoting @Skillz4Killz
This is an oversight that we as developers can’t even deal with! MADNESS!
I completely agree with @Skillz4Killz here, the error output is not helpful at all with debugging. Just a better stack trace would be enough to help developers debug their code.
The linter didn’t tell me that I had an async call without a
try {} catch {}or a.catch()at all. It just treats it like it’s not an issue when in fact it’s almost the worst mistake one can make. Even runningdeno linton the file just ignores any unawaited calls.Yup. And if it keeps going like this it’s only going to get worse. Everybody makes mistakes. At least make them possible to debug.
My use case is a tool that serves as a container for running multiple 3rd-party web handler scripts. I need to be able to keep the server running (and capture and report the error to the user myself) if one of the scripts forgets to
catchone of their promises deep in their handler.Panicking on every unhandled rejection is certainly a reasonable default, but should be configurable either when starting the
denoprocess, or at runtime (e.g. viaonunhandledrejection) - to be able to support the use cases that require it.Note to self:
PromiseRejectionEventclass and its typingsDeno.core.setPromiseRejectCallbackin99_main.jsafter runtime initialization that will dispatchPromiseRejectionEventand check if event was halted (ie.preventDefault()was called on the event)defineEventHandler(window, "unhandledrejection");in99_main.jsdeno testsubcommandI believe processes, in general, should not be responsible for restarting themselves. That’s the responsibility of the underlying platform the processes are running at (see container orchestration or process managers).
Similarly, in response to other fellows, process crashing in production when there are unhandled exceptions is the safest bet. Since the process should be automatically restarted by its manager and restarting should be the best way to ensure the process is properly initialized again.
Landed in #12994, it will be released in v1.24 on July, 20th.
It was released in v1.24 https://deno.com/blog/v1.24#unhandledrejection-event
@lucacasonato I think uncaught rejections absolutely fall under “uncaught errors”.
Let’s say we tried to mirror the browser behaviour by not exiting on a rejection if and only if there is an
uncaughtrejectionhandler which callsrejectionEvent.preventDefault(). Does that count as “catching” the error in a way? If so, we’re not breaking our rule.Nevertheless, I’m against this being usable to prevent exits on rejections. I think we are still breaking our rule at the language level and this is a foot-gun. Note that browsers don’t kill the tab on error either way, they only use this to decide whether or not to output the error to the console. So it’s not so simple to draw an analogy.