Uh oh!
There was an error while loading. Please reload this page.
events: implement captureRejections for async handlers - #27867
Conversation
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
You can just use Reflect.apply, since Reflect here is coming from primordials
There was a problem hiding this comment.
That's what's giving us the speed bump.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
this property only able on async function, why don't rename it to asyncCatch? 🤔
There was a problem hiding this comment.
you could have a normal function that returns a promise
There was a problem hiding this comment.
@devsnek arguably a normal function that returns a promise is an async function :P
benjamingr
commented
May 25, 2019
This looks great it will take me a few days to think this over and review 😊 |
benjamingr
left a comment
There was a problem hiding this comment.
First batch before taking a look at the code.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
I would prefer an explicit name like captureRejections or something that indicates that rejections are converted to errors.
There was a problem hiding this comment.
Please add a test on how this works in subclasses of EventEmitter? This is scary for libraries to do otherwise I think.
There was a problem hiding this comment.
| setting change this behavior, installing a `.catch()` handler on the | |
| setting change this behavior, installing an error handler on the |
There was a problem hiding this comment.
Honestly this would require someone to return a promise from an event emitter method and add a catch method to it later which means it's not an async function.
I am entirely fine with not supporting this case or emitting an event here just because the added complexity likely isn't worth it.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
I think that if we keep this symbol it's likely better to keep it on events rather than the global symbol registry like util.promisify.custom unless there is a specific need for it to behave well across realms.
There was a problem hiding this comment.
Keeping it on the global symbol registry allows it to be used browser side as well, which is going to be important for things like readable-stream and the various browser ports of EventEmitter. That said, exposing the symbol as a convenience property makes sense... e.g.
const{ EventEmitter, rejection }=require('events')ee2.on(rejection,console.log)// would be equivalent toee2.on(Symbol.for('nodejs.rejection'),console.log)// either would workThere was a problem hiding this comment.
I'm a bit unsure about exporting it as a symbol, mainly because we have a global switch at events.captureRejections, and the Symbol at events.rejection. I think this can get confusing very quickly.
There was a problem hiding this comment.
@devsnek arguably a normal function that returns a promise is an async function :P
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
benjamingr
commented
May 25, 2019
Random thought: we can check if something is an In this case (which is by far the most common one) unlike a regular |
mcollina
commented
May 25, 2019
I thought a lot about that. However that would be hard for test frameworks, including our own in core
I would like discuss to turn |
benjamingr
commented
May 25, 2019
What about "functions we know return promises no one adds a |
mcollina
commented
May 25, 2019
I don't understand. You mean functions that purposefully would like to trigger an |
Uh oh!
There was an error while loading. Please reload this page.
PR-URL: #27867 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Michaël Zasso <targos@protonmail.com>
mcollina
commented
Dec 3, 2019
Landed in 8cf8eb1...415bba7 🎉 |
PR-URL: #27867 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Michaël Zasso <targos@protonmail.com>
PR-URL: #27867 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Michaël Zasso <targos@protonmail.com>
PR-URL: #27867 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Michaël Zasso <targos@protonmail.com>
PR-URL: #27867 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Michaël Zasso <targos@protonmail.com>
PR-URL: #27867 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Michaël Zasso <targos@protonmail.com>
PR-URL: #27867 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Michaël Zasso <targos@protonmail.com>
PR-URL: #27867 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Michaël Zasso <targos@protonmail.com>
This is a security release. This release includes a single commit, an update to npm to 6.13.4. For more details about the vulnerability please consult the npm blog: https://blog.npmjs.org/post/189618601100/binary-planting-with-the-npm-cli Notable Changes: * deps: - update npm to 6.13.4 #30904 - update uvwasi (Anna Henningsen) #30745 - upgrade to libuv 1.34.0 (Colin Ihrig) #30783 * doc: - docs deprecate http finished (Robert Nagy) #28679 * events: - add captureRejection option (Matteo Collina) #27867 * http: - add captureRejection support (Matteo Collina) #27867 - llhttp opt-in insecure HTTP header parsing (Sam Roberts) #30567 * http2: - implement capture rection for 'request' and 'stream' events (Matteo Collina) #27867 * net: - implement capture rejections for 'connection' event (Matteo Collina) #27867 * repl: - support previews by eager evaluating input (Ruben Bridgewater) #30811 * stream: - add support for captureRejection option (Matteo Collina) #27867 * tls: - implement capture rejections for 'secureConnection' event (Matteo Collina) #27867 - expose IETF name for current cipher suite (Sam Roberts) #30637 * worker: - add argv constructor option (legendecas) #30559 PR-URL: #30937
This is a security release. This release includes a single commit, an update to npm to 6.13.4. For more details about the vulnerability please consult the npm blog: https://blog.npmjs.org/post/189618601100/binary-planting-with-the-npm-cli Notable Changes: * deps: * update npm to 6.13.4 #30904 * update uvwasi (Anna Henningsen) #30745 * upgrade to libuv 1.34.0 (Colin Ihrig) #30783 * doc: * docs deprecate http finished (Robert Nagy) #28679 * events: * add captureRejection option (Matteo Collina) #27867 * http: * add captureRejection support (Matteo Collina) #27867 * llhttp opt-in insecure HTTP header parsing (Sam Roberts) #30567 * http2: * implement capture rection for 'request' and 'stream' events (Matteo Collina) #27867 * net: * implement capture rejections for 'connection' event (Matteo Collina) #27867 * repl: * support previews by eager evaluating input (Ruben Bridgewater) #30811 * stream: * add support for captureRejection option (Matteo Collina) #27867 * tls: * implement capture rejections for 'secureConnection' event (Matteo Collina) #27867 * expose IETF name for current cipher suite (Sam Roberts) #30637 * worker: * add argv constructor option (legendecas) #30559 PR-URL: #30937
This is a security release. For more details about the vulnerability please consult the npm blog: https://blog.npmjs.org/post/189618601100/binary-planting-with-the-npm-cli Notable Changes: * deps: * update npm to 6.13.4 #30904 * update uvwasi (Anna Henningsen) #30745 * upgrade to libuv 1.34.0 (Colin Ihrig) #30783 * doc: * docs deprecate http finished (Robert Nagy) #28679 * events: * add captureRejection option (Matteo Collina) #27867 * http: * add captureRejection support (Matteo Collina) #27867 * llhttp opt-in insecure HTTP header parsing (Sam Roberts) #30567 * http2: * implement capture rection for 'request' and 'stream' events (Matteo Collina) #27867 * net: * implement capture rejections for 'connection' event (Matteo Collina) #27867 * repl: * support previews by eager evaluating input (Ruben Bridgewater) #30811 * stream: * add support for captureRejection option (Matteo Collina) #27867 * tls: * implement capture rejections for 'secureConnection' event (Matteo Collina) #27867 * expose IETF name for current cipher suite (Sam Roberts) #30637 * worker: * add argv constructor option (legendecas) #30559 PR-URL: #30937
This is a security release. For more details about the vulnerability please consult the npm blog: https://blog.npmjs.org/post/189618601100/binary-planting-with-the-npm-cli Notable Changes: * deps: * update npm to 6.13.4 #30904 * update uvwasi (Anna Henningsen) #30745 * upgrade to libuv 1.34.0 (Colin Ihrig) #30783 * doc: * docs deprecate http finished (Robert Nagy) #28679 * events: * add captureRejection option (Matteo Collina) #27867 * http: * add captureRejection support (Matteo Collina) #27867 * llhttp opt-in insecure HTTP header parsing (Sam Roberts) #30567 * http2: * implement capture rection for 'request' and 'stream' events (Matteo Collina) #27867 * net: * implement capture rejections for 'connection' event (Matteo Collina) #27867 * repl: * support previews by eager evaluating input (Ruben Bridgewater) #30811 * stream: * add support for captureRejection option (Matteo Collina) #27867 * tls: * implement capture rejections for 'secureConnection' event (Matteo Collina) #27867 * expose IETF name for current cipher suite (Sam Roberts) #30637 * worker: * add argv constructor option (legendecas) #30559 PR-URL: #30937
PR-URL: #27867 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Michaël Zasso <targos@protonmail.com>
PR-URL: #27867 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Michaël Zasso <targos@protonmail.com>
PR-URL: #27867 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Michaël Zasso <targos@protonmail.com>
One of the biggest source of issues with
'unhandledRejection'is the use ofEventEmitterin combination with anasyncfunction (see https://github.com/mcollina/make-promises-safe for details). Currently, there is no way to catch a rejection when it is emitted within an event handler, causing hard to track bugs and memory leaks. Our current best practice is to always wrap the content of anasyncfunction in a try/catch block and handle errors, but this is error prone.This PR adds a new
captureRejectionsoption toEventEmitteras well as a way to override the global default. If anEventEmitteris created withcaptureRejectionsenabled, a.catch()handler is added every time aPromiseis returned from an event handler: if a rejection happens, the'error'event is triggered, avoiding the'unhandledRejection'. An implementer can also listen toSymbol.for('nodejs.rejection')to automatically dispose of resources, and in that case'error'is not emitted (Streams will leverage this in the future).Example:
This has also the side benefit of greatly improving our microbenchmarks, up to 88% in one case:
Details
I have not done a deep investigation on why, but I think it's due to the retrieval of
Reflect.applyfrom theReflectobject. Even if this change gets rejected, I think we should investigate how to leverage the perf improvements alone.Checklist
make -j4 test(UNIX), orvcbuild test(Windows) passes