Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

events: implement captureRejections for async handlers #27867

Closed
wants to merge 7 commits into from

Conversation

mcollina
Copy link
Member

@mcollina mcollina commented May 25, 2019

One of the biggest source of issues with 'unhandledRejection' is the use of EventEmitter in combination with an async function (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 an async function in a try/catch block and handle errors, but this is error prone.

This PR adds a new captureRejections option to EventEmitter as well as a way to override the global default. If an EventEmitter is created with captureRejections enabled, a .catch() handler is added every time a Promise is returned from an event handler: if a rejection happens, the 'error' event is triggered, avoiding the 'unhandledRejection'. An implementer can also listen to Symbol.for('nodejs.rejection') to automatically dispose of resources, and in that case 'error' is not emitted (Streams will leverage this in the future).

Example:

const ee1 = new EventEmitter({ captureRejections: true });
ee1.on('something', async (value) => {
  throw new Error('kaboom');
});

ee1.on('error', console.log);

const ee2 = new EventEmitter({ captureRejections: true });
ee2.on('something', async (value) => {
  throw new Error('kaboom');
});

ee2.on(Symbol.for('nodejs.rejection'), console.log);

EventEmitter.captureRejections = true;
const ee3 = new EventEmitter();
ee3.on('something', async (value) => {
  throw new Error('kaboom');
});

ee3.on('error', console.log);

This has also the side benefit of greatly improving our microbenchmarks, up to 88% in one case:

                                                     confidence improvement accuracy (*)   (**)  (***)
 events/ee-add-remove.js n=1000000                                   1.54 %       ±2.98% ±4.02% ±5.33%
 events/ee-emit.js listeners=1 argc=0 n=2000000             ***     34.13 %       ±0.69% ±0.92% ±1.20%
 events/ee-emit.js listeners=1 argc=10 n=2000000            ***     12.08 %       ±1.63% ±2.19% ±2.88%
 events/ee-emit.js listeners=1 argc=2 n=2000000             ***      9.37 %       ±1.36% ±1.83% ±2.43%
 events/ee-emit.js listeners=1 argc=4 n=2000000             ***      8.17 %       ±3.18% ±4.27% ±5.65%
 events/ee-emit.js listeners=10 argc=0 n=2000000            ***     88.93 %       ±0.71% ±0.96% ±1.26%
 events/ee-emit.js listeners=10 argc=10 n=2000000           ***     22.19 %       ±0.95% ±1.27% ±1.66%
 events/ee-emit.js listeners=10 argc=2 n=2000000            ***     26.07 %       ±0.54% ±0.72% ±0.94%
 events/ee-emit.js listeners=10 argc=4 n=2000000            ***     22.78 %       ±0.57% ±0.76% ±0.99%
 events/ee-emit.js listeners=5 argc=0 n=2000000             ***     64.96 %       ±3.82% ±5.14% ±6.82%
 events/ee-emit.js listeners=5 argc=10 n=2000000            ***     24.33 %       ±0.88% ±1.17% ±1.53%
 events/ee-emit.js listeners=5 argc=2 n=2000000             ***     24.50 %       ±1.04% ±1.38% ±1.81%
 events/ee-emit.js listeners=5 argc=4 n=2000000             ***     26.23 %       ±1.88% ±2.52% ±3.31%
 events/ee-listener-count-on-prototype.js n=50000000                 0.33 %       ±0.72% ±0.96% ±1.25%
 events/ee-listeners-many.js n=5000000                               0.98 %       ±1.60% ±2.12% ±2.76%
 events/ee-listeners.js n=5000000                                    0.04 %       ±1.10% ±1.46% ±1.91%
 events/ee-once.js n=20000000                               ***      6.17 %       ±2.24% ±3.02% ±3.99%

Be aware that when doing many comparisons the risk of a false-positive
result increases. In this case there are 17 comparisons, you can thus
expect the following amount of false-positive results:
  0.85 false positives, when considering a   5% risk acceptance (*, **, ***),
  0.17 false positives, when considering a   1% risk acceptance (**, ***),
  0.02 false positives, when considering a 0.1% risk acceptance (***)

I have not done a deep investigation on why, but I think it's due to the retrieval of Reflect.apply from the Reflect object. Even if this change gets rejected, I think we should investigate how to leverage the perf improvements alone.

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines

@mcollina mcollina added events semver-minor labels May 25, 2019
lib/events.js Outdated Show resolved Hide resolved
lib/events.js Outdated Show resolved Hide resolved
doc/api/events.md Outdated Show resolved Hide resolved
lib/events.js Outdated
@@ -112,12 +112,13 @@ EventEmitter.init = function(opts) {
}
};

const apply = Reflect.apply;
Copy link
Member

@devsnek devsnek May 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can just use Reflect.apply, since Reflect here is coming from primordials

Copy link
Member Author

@mcollina mcollina May 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what's giving us the speed bump.

doc/api/events.md Outdated Show resolved Hide resolved
doc/api/events.md Outdated Show resolved Hide resolved
@@ -169,6 +220,11 @@ const EventEmitter = require('events');
All `EventEmitter`s emit the event `'newListener'` when new listeners are
added and `'removeListener'` when existing listeners are removed.

It supports the following option:

* `autoCatch`, it can be `true` or `false`. It enables [automatic catch
Copy link
Member

@Himself65 Himself65 May 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this property only able on async function, why don't rename it to asyncCatch? 🤔

Copy link
Member

@devsnek devsnek May 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could have a normal function that returns a promise

Copy link
Member

@benjamingr benjamingr May 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@devsnek arguably a normal function that returns a promise is an async function :P

@benjamingr
Copy link
Member

@benjamingr benjamingr commented May 25, 2019

This looks great it will take me a few days to think this over and review 😊

Copy link
Member

@benjamingr benjamingr left a comment

First batch before taking a look at the code.

doc/api/events.md Outdated Show resolved Hide resolved
doc/api/events.md Outdated Show resolved Hide resolved
@@ -155,9 +155,63 @@ myEmitter.emit('error', new Error('whoops!'));
// Prints: whoops! there was an error
```

## AutoCatch of Promise rejection
Copy link
Member

@benjamingr benjamingr May 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer an explicit name like captureRejections or something that indicates that rejections are converted to errors.

});
```

The `autoCatch` option in the `EventEmitter` constructor or the global
Copy link
Member

@benjamingr benjamingr May 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a test on how this works in subclasses of EventEmitter? This is scary for libraries to do otherwise I think.

```

The `autoCatch` option in the `EventEmitter` constructor or the global
setting change this behavior, installing a `.catch()` handler on the
Copy link
Member

@benjamingr benjamingr May 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
setting change this behavior, installing a `.catch()` handler on the
setting change this behavior, installing an error handler on the

The `autoCatch` option in the `EventEmitter` constructor or the global
setting change this behavior, installing a `.catch()` handler on the
`Promise`. This `catch` handler routes the exception to the
[`Symbol.for('nodejs.rejection')`][rejection] event if there is a listener, or to
Copy link
Member

@benjamingr benjamingr May 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

doc/api/events.md Show resolved Hide resolved
throw new Error('kaboom');
});

ee2.on(Symbol.for('nodejs.rejection'), console.log);
Copy link
Member

@benjamingr benjamingr May 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Member

@jasnell jasnell Aug 6, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 to

ee2.on(Symbol.for('nodejs.rejection'), console.log)

// either would work

Copy link
Member Author

@mcollina mcollina Oct 2, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@@ -169,6 +220,11 @@ const EventEmitter = require('events');
All `EventEmitter`s emit the event `'newListener'` when new listeners are
added and `'removeListener'` when existing listeners are removed.

It supports the following option:

* `autoCatch`, it can be `true` or `false`. It enables [automatic catch
Copy link
Member

@benjamingr benjamingr May 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@devsnek arguably a normal function that returns a promise is an async function :P

lib/events.js Outdated Show resolved Hide resolved
lib/events.js Outdated Show resolved Hide resolved
lib/events.js Outdated Show resolved Hide resolved
@benjamingr
Copy link
Member

@benjamingr benjamingr commented May 25, 2019

Random thought: we can check if something is an AsyncFunction passed to the event emitter, if it is we know that there is no catch handler attached to it because we have invoked the function ourselves.

In this case (which is by far the most common one) unlike a regular unhandledRejection I think we should emit error by default in a future Node.js version and I'd like to discuss this and/or vote on this in the summit if possible :]

@mcollina
Copy link
Member Author

@mcollina mcollina commented May 25, 2019

Random thought: we can check if something is an AsyncFunction passed to the event emitter, if it is we know that there is no catch handler attached to it because we have invoked the function ourselves.

I thought a lot about that. However that would be hard for test frameworks, including our own in core common.mustCall(async function() {}) returns a function() {}. Moreover, that would not be safe against refactoring, as moving some code around can change where the promise come from. In other terms, I think we should not have a different behavior between functions that returns a promise and async functions, even thought the goal of this PR is to cover async functions.


In this case (which is by far the most common one) unlike a regular unhandledRejection I think we should emit error by default in a future Node.js version and I'd like to discuss this and/or vote on this in the summit if possible :]

I would like discuss to turn EventEmitter.autoCatch = true by default in 13. At the beginning I thought to make this a semver-major PR, but @MylesBorins made the point that making it semver-minor means it could be backported down to 10.

@benjamingr
Copy link
Member

@benjamingr benjamingr commented May 25, 2019

In other terms, I think we should not have a different behavior between functions that returns a promise and async functions, even thought the goal of this PR is to cover async functions

What about "functions we know return promises no one adds a catch to :)?

@mcollina
Copy link
Member Author

@mcollina mcollina commented May 25, 2019

What about "functions we know return promises no one adds a catch to :)?

I don't understand. You mean functions that purposefully would like to trigger an 'unhandledRejection'?

lib/events.js Show resolved Hide resolved
@Trott Trott added the review wanted label May 30, 2019
MylesBorins added a commit that referenced this issue Dec 17, 2019
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
targos pushed a commit that referenced this issue Jan 14, 2020
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>
targos pushed a commit that referenced this issue Jan 14, 2020
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>
targos pushed a commit that referenced this issue Jan 14, 2020
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>
targos pushed a commit that referenced this issue Jan 14, 2020
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>
targos pushed a commit that referenced this issue Jan 14, 2020
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>
targos pushed a commit that referenced this issue Jan 14, 2020
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>
targos pushed a commit that referenced this issue Jan 14, 2020
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>
BethGriggs pushed a commit that referenced this issue Feb 6, 2020
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>
BethGriggs pushed a commit that referenced this issue Feb 6, 2020
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>
BethGriggs pushed a commit that referenced this issue Feb 6, 2020
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>
BethGriggs pushed a commit that referenced this issue Feb 6, 2020
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>
BethGriggs pushed a commit that referenced this issue Feb 6, 2020
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>
BethGriggs pushed a commit that referenced this issue Feb 6, 2020
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>
BethGriggs pushed a commit that referenced this issue Feb 6, 2020
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>
@MylesBorins MylesBorins mentioned this pull request Feb 8, 2020
targos pushed a commit that referenced this issue Feb 11, 2020
Notable changes:

New assert APIs

The `assert` module now provides experimental `assert.match()` and
`assert.doesNotMatch()` methods. They will validate that the first argument is a
string and matches (or does not match) the provided regular expression

This is an experimental feature.

Ruben Bridgewater [#30929](#30929).

Advanced serialization for IPC

The `child_process` and `cluster` modules now support a `serialization` option
to change the serialization mechanism used for IPC. The option can have one of
two values:

* `'json'` (default): `JSON.stringify()` and `JSON.parse()` are used. This is
  how message serialization was done before.
* `'advanced'`: The serialization API of the `v8` module is used. It is based on
  the HTML structured clone algorithm.
  and is able to serialize more built-in JavaScript object types, such as
  `BigInt`, `Map`, `Set` etc. as well as circular data structures.

Anna Henningsen [#30162](#30162).

CLI flags

The new `--trace-exit` CLI flag makes Node.js print a stack trace whenever the
Node.js environment is exited proactively (i.e. by invoking the `process.exit()`
function or pressing Ctrl+C).

legendecas [#30516](#30516).

___

The new `--trace-uncaught` CLI flag makes Node.js print a stack trace at the
time of throwing uncaught exceptions, rather than at the creation of the `Error`
object, if there is any.
This option is not enabled by default because it may affect garbage collection
behavior negatively.

Anna Henningsen [#30025](#30025).

___

The `--disallow-code-generation-from-strings` V8 CLI flag is now whitelisted in
the `NODE_OPTIONS` environment variable.

Shelley Vohr [#30094](#30094).

New crypto APIs

For DSA and ECDSA, a new signature encoding is now supported in addition to the
existing one (DER). The `verify` and `sign` methods accept a `dsaEncoding`
option, which can have one of two values:

* `'der'` (default): DER-encoded ASN.1 signature structure encoding `(r, s)`.
* `'ieee-p1363'`: Signature format `r || s` as proposed in IEEE-P1363.

Tobias Nießen [#29292](#29292).

___

A new method was added to `Hash`: `Hash.prototype.copy`. It makes it possible to
clone the internal state of a `Hash` object into a new `Hash` object, allowing
to compute the digest between updates.

Ben Noordhuis [#29910](#29910).

Dependency updates

libuv was updated to 1.34.0. This includes fixes to `uv_fs_copyfile()` and
`uv_interface_addresses()` and adds two new functions: `uv_sleep()` and
`uv_fs_mkstemp()`.

Colin Ihrig [#30783](#30783).

___

V8 was updated to 7.8.279.23. This includes performance improvements to object
destructuring, RegExp match failures and WebAssembly startup time.
The official release notes are available at https://v8.dev/blog/v8-release-78.

Michaël Zasso [#30109](#30109).

New EventEmitter APIs

The new `EventEmitter.on` static method allows to async iterate over events.

Matteo Collina [#27994](#27994).

___

It is now possible to monitor `'error'` events on an `EventEmitter` without
consuming the emitted error by installing a listener using the symbol
`EventEmitter.errorMonitor`.

Gerhard Stoebich [#30932](#30932).

___

Using `async` functions with event handlers is problematic, because it
can lead to an unhandled rejection in case of a thrown exception.

The experimental `captureRejections` option in the `EventEmitter` constructor or
the global setting change this behavior, installing a
`.then(undefined, handler)` handler on the `Promise`. This handler routes the
exception asynchronously to the `Symbol.for('nodejs.rejection')` method if there
is one, or to the `'error'` event handler if there is none.

Setting `EventEmitter.captureRejections = true` will change the default for all
new instances of `EventEmitter`.

This is an experimental feature.

Matteo Collina [#27867](#27867).

Performance Hooks are no longer experimental

The `perf_hooks` module is now considered a stable API.

legendecas [#31101](#31101).

Introduction of experimental WebAssembly System Interface (WASI) support

A new core module, `wasi`, is introduced to provide an implementation of the
[WebAssembly System Interface](https://wasi.dev/) specification.
WASI gives sandboxed WebAssembly applications access to the
underlying operating system via a collection of POSIX-like functions.

This is an experimental feature.

Colin Ihrig [#30258](#30258).

PR-URL: #31691
targos pushed a commit that referenced this issue Feb 11, 2020
Notable changes:

New assert APIs

The `assert` module now provides experimental `assert.match()` and
`assert.doesNotMatch()` methods. They will validate that the first argument is a
string and matches (or does not match) the provided regular expression

This is an experimental feature.

Ruben Bridgewater [#30929](#30929).

Advanced serialization for IPC

The `child_process` and `cluster` modules now support a `serialization` option
to change the serialization mechanism used for IPC. The option can have one of
two values:

* `'json'` (default): `JSON.stringify()` and `JSON.parse()` are used. This is
  how message serialization was done before.
* `'advanced'`: The serialization API of the `v8` module is used. It is based on
  the HTML structured clone algorithm.
  and is able to serialize more built-in JavaScript object types, such as
  `BigInt`, `Map`, `Set` etc. as well as circular data structures.

Anna Henningsen [#30162](#30162).

CLI flags

The new `--trace-exit` CLI flag makes Node.js print a stack trace whenever the
Node.js environment is exited proactively (i.e. by invoking the `process.exit()`
function or pressing Ctrl+C).

legendecas [#30516](#30516).

___

The new `--trace-uncaught` CLI flag makes Node.js print a stack trace at the
time of throwing uncaught exceptions, rather than at the creation of the `Error`
object, if there is any.
This option is not enabled by default because it may affect garbage collection
behavior negatively.

Anna Henningsen [#30025](#30025).

___

The `--disallow-code-generation-from-strings` V8 CLI flag is now whitelisted in
the `NODE_OPTIONS` environment variable.

Shelley Vohr [#30094](#30094).

New crypto APIs

For DSA and ECDSA, a new signature encoding is now supported in addition to the
existing one (DER). The `verify` and `sign` methods accept a `dsaEncoding`
option, which can have one of two values:

* `'der'` (default): DER-encoded ASN.1 signature structure encoding `(r, s)`.
* `'ieee-p1363'`: Signature format `r || s` as proposed in IEEE-P1363.

Tobias Nießen [#29292](#29292).

___

A new method was added to `Hash`: `Hash.prototype.copy`. It makes it possible to
clone the internal state of a `Hash` object into a new `Hash` object, allowing
to compute the digest between updates.

Ben Noordhuis [#29910](#29910).

Dependency updates

libuv was updated to 1.34.0. This includes fixes to `uv_fs_copyfile()` and
`uv_interface_addresses()` and adds two new functions: `uv_sleep()` and
`uv_fs_mkstemp()`.

Colin Ihrig [#30783](#30783).

___

V8 was updated to 7.8.279.23. This includes performance improvements to object
destructuring, RegExp match failures and WebAssembly startup time.
The official release notes are available at https://v8.dev/blog/v8-release-78.

Michaël Zasso [#30109](#30109).

New EventEmitter APIs

The new `EventEmitter.on` static method allows to async iterate over events.

Matteo Collina [#27994](#27994).

___

It is now possible to monitor `'error'` events on an `EventEmitter` without
consuming the emitted error by installing a listener using the symbol
`EventEmitter.errorMonitor`.

Gerhard Stoebich [#30932](#30932).

___

Using `async` functions with event handlers is problematic, because it
can lead to an unhandled rejection in case of a thrown exception.

The experimental `captureRejections` option in the `EventEmitter` constructor or
the global setting change this behavior, installing a
`.then(undefined, handler)` handler on the `Promise`. This handler routes the
exception asynchronously to the `Symbol.for('nodejs.rejection')` method if there
is one, or to the `'error'` event handler if there is none.

Setting `EventEmitter.captureRejections = true` will change the default for all
new instances of `EventEmitter`.

This is an experimental feature.

Matteo Collina [#27867](#27867).

Performance Hooks are no longer experimental

The `perf_hooks` module is now considered a stable API.

legendecas [#31101](#31101).

Introduction of experimental WebAssembly System Interface (WASI) support

A new core module, `wasi`, is introduced to provide an implementation of the
[WebAssembly System Interface](https://wasi.dev/) specification.
WASI gives sandboxed WebAssembly applications access to the
underlying operating system via a collection of POSIX-like functions.

This is an experimental feature.

Colin Ihrig [#30258](#30258).

PR-URL: #31691
goto-bus-stop added a commit to browserify/events that referenced this issue Feb 28, 2021
goto-bus-stop added a commit to browserify/events that referenced this issue Feb 28, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
events notable-change review wanted semver-minor
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet