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: support EventTarget in `once` #29498

Closed
wants to merge 12 commits into from

Conversation

@JeniaBR
Copy link
Contributor

commented Sep 8, 2019

My attempt to add support for EventTarget in the once static method.
This PR follow up #27977

Ping @benjamingr please take a look.

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
doc/api/events.md Outdated Show resolved Hide resolved
lib/events.js Outdated Show resolved Hide resolved
Copy link
Member

left a comment

Looks good mostly! :)

doc/api/events.md Outdated Show resolved Hide resolved
lib/events.js Outdated Show resolved Hide resolved
@JeniaBR JeniaBR requested review from mscdex, devsnek, benjamingr and addaleax Sep 10, 2019
lib/events.js Outdated Show resolved Hide resolved
@nodejs-github-bot

This comment has been minimized.

Copy link
Member

left a comment

Last iteration lgtm :]

Thanks for following up on #27977 hope to see you around again 🙏

@JeniaBR

This comment has been minimized.

Copy link
Contributor Author

commented Sep 11, 2019

@benjamingr @addaleax Thanks!
How can I fix the commit messages in order to pass Travis CI? :)

@mscdex

This comment has been minimized.

Copy link
Contributor

commented Sep 11, 2019

@JeniaBR You might find core-validate-commit to be of help with that.

@JeniaBR JeniaBR force-pushed the JeniaBR:event-emitter-once-event-target branch 2 times, most recently from d3938a4 to 836d846 Sep 11, 2019
@JeniaBR

This comment has been minimized.

Copy link
Contributor Author

commented Sep 11, 2019

Travis pass 👍
What's next? @addaleax @benjamingr @mscdex

@mcollina

This comment has been minimized.

Copy link
Member

commented Sep 11, 2019

I think this needs a rebase, as it shows 361 commits.

@@ -84,10 +84,72 @@ async function onceError() {
strictEqual(ee.listenerCount('myevent'), 0);
}

async function onceWithEventTarget() {
const emitter = new class EventTargetLike extends EventEmitter {

This comment has been minimized.

Copy link
@mcollina

mcollina Sep 11, 2019

Member

I'm not sure this is the right approach. I would prefer this not to extend EventEmitter.
Also, you can make it a top level helper.

This comment has been minimized.

Copy link
@JeniaBR

JeniaBR Sep 11, 2019

Author Contributor

@mcollina I thought about implementing partial EventTarget and use it for testing.
Something like that EventTarget Example (Also, need to implement in that example above once functionality).
This approach seems to be a little bit exaggerated.

WDYT I should do here?
(If we decide to keep it like that, I will extract it to a top level helper)

This comment has been minimized.

Copy link
@mcollina

mcollina Sep 13, 2019

Member

I'm -1 to have the EventTarget example extend EventEmitter. We do not need a full EventTarget implementation, but we could just mock/place assertions on the methods that are used by once.

This comment has been minimized.

Copy link
@JeniaBR

JeniaBR Sep 14, 2019

Author Contributor

done.

errorListener = (err) => {
emitter.removeListener(name, eventListener);
reject(err);
if (typeof emitter.addEventListener !== 'function') {

This comment has been minimized.

Copy link
@mcollina

mcollina Sep 11, 2019

Member

I would prefer this to be on the positive side instead. "if is EventTarget, then ...".

This comment has been minimized.

Copy link
@JeniaBR

JeniaBR Sep 11, 2019

Author Contributor

done.

@JeniaBR

This comment has been minimized.

Copy link
Contributor Author

commented Sep 11, 2019

I think this needs a rebase, as it shows 361 commits.

@mcollina How can I do it properly?

I synchronized my master with upstream/master, then I tried to rebase my master with this branch and I get a lot of merge conflicts.
I think I'm doing something wrong here.

@JeniaBR JeniaBR requested a review from mcollina Sep 11, 2019
doc/api/events.md Outdated Show resolved Hide resolved
lib/events.js Show resolved Hide resolved
event or that is rejected when the `EventEmitter` emits `'error'`.
The `Promise` will resolve with an array of all the arguments emitted to the
given event.

Note: This method is intentionally generic and works with the web platform
[EventTarget](WHATWG-EventTarget) interface, which have no special
`'error'` event semantics and do not listen to the `'error'` event.

This comment has been minimized.

Copy link
@jasnell

jasnell Sep 11, 2019

Member

Unless I'm missing something, an example showing the use of the EventTarget API would be helpful.

This comment has been minimized.

Copy link
@JeniaBR

JeniaBR Sep 12, 2019

Author Contributor
const { once } = require('events');
const EventTarget = require('../event-target-implementation')

async function run() {
  const et = new EventTarget();

  process.nextTick(() => {
    et.dispatchEvent('myevent', 42);
  });

  const [value] = await once(et, 'myevent');
  console.log(value);
}

run();

@jasnell something like that?
Not sure about this line const EventTarget = require('../event-target-implementation').

This comment has been minimized.

Copy link
@benjamingr

benjamingr Sep 12, 2019

Member

I think we can defer doing that to a future pull request. Mostly this is for web compatibility and for making EE more universal JavaScript compatible but our docs on the other hand mostly ignore that as far as I know.

@benjamingr

This comment has been minimized.

Copy link
Member

commented Sep 11, 2019

I think I'm doing something wrong here.

You did a merge which we typically don't do in Node.js - the process is rather than having a merge commit we do a rebase which places the commits on top of all the commits to this point.

The way to go about this would be cherry picking the relevant commits (git cherry-pick COMMIT) on top of checked out master. Alternatively you can just re-apply the changes on top of master (I am not concerned about attribution for my part and you can just re-apply the changes on master).

Copy link
Member

left a comment

LGTM with an example in the docs added

@mcollina

This comment has been minimized.

Copy link
Member

commented Sep 14, 2019

Good workb

@Trott

This comment has been minimized.

Copy link
Member

commented Sep 16, 2019

@addaleax @jasnell @mcollina @benjamingr What's the semverity of this? Patch? Minor?

@nodejs-github-bot

This comment has been minimized.

@Trott Trott force-pushed the nodejs:master branch from 1ecc406 to 49cf67e Sep 17, 2019
@nodejs-github-bot

This comment has been minimized.

@benjamingr

This comment has been minimized.

Copy link
Member

commented Sep 18, 2019

@Trott minor on the safe side IMO, this doesn't really introduce an API and could very theoretically break things but in practice shouldn't.

This could theoretically be seen as introducing an API. I am also fine with patch.

@Trott Trott added the semver-minor label Sep 18, 2019
@nodejs-github-bot

This comment has been minimized.

Trott added a commit that referenced this pull request Sep 23, 2019
PR-URL: #29498
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Minwoo Jung <minwoo@nodesource.com>
@Trott

This comment has been minimized.

Copy link
Member

commented Sep 23, 2019

Landed in 34a61d5

@Trott Trott closed this Sep 23, 2019
targos added a commit that referenced this pull request Sep 23, 2019
PR-URL: #29498
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Minwoo Jung <minwoo@nodesource.com>
BridgeAR added a commit that referenced this pull request Sep 24, 2019
Notable changes:

* crypto:
  * Add `oaepLabel` option #29489
* deps:
  * Update V8 to 7.7.299.11 #28918
    * More efficient memory handling
    * Stack trace serialization got faster
    * The `Intl.NumberFormat` API gained new functionality
    * For more information: https://v8.dev/blog/v8-release-77
* events:
  * Add support for `EventTarget` in `once`
    #29498
* fs:
  * Expose memory file mapping flag `UV_FS_O_FILEMAP`
    #29260
* inspector:
  * New API - `Session.connectToMainThread`
    #28870
* process:
  * Initial SourceMap support via `env.NODE_V8_COVERAGE`
    #28960
* stream:
  * Make `_write()` optional when `_writev()` is implemented
    #29639
* tls:
  * Add option to override signature algorithms
    #29598
* util:
  * Add `encodeInto` to `TextEncoder`
    #29524
* worker:
  * The `worker_thread` module is now stable
    #29512
@BridgeAR BridgeAR referenced this pull request Sep 24, 2019
BridgeAR added a commit that referenced this pull request Sep 25, 2019
PR-URL: #29498
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Minwoo Jung <minwoo@nodesource.com>
BridgeAR added a commit that referenced this pull request Sep 25, 2019
Notable changes:

* crypto:
  * Add `oaepLabel` option #29489
* deps:
  * Update V8 to 7.7.299.11 #28918
    * More efficient memory handling
    * Stack trace serialization got faster
    * The `Intl.NumberFormat` API gained new functionality
    * For more information: https://v8.dev/blog/v8-release-77
* events:
  * Add support for `EventTarget` in `once`
    #29498
* fs:
  * Expose memory file mapping flag `UV_FS_O_FILEMAP`
    #29260
* inspector:
  * New API - `Session.connectToMainThread`
    #28870
* process:
  * Initial SourceMap support via `env.NODE_V8_COVERAGE`
    #28960
* stream:
  * Make `_write()` optional when `_writev()` is implemented
    #29639
* tls:
  * Add option to override signature algorithms
    #29598
* util:
  * Add `encodeInto` to `TextEncoder`
    #29524
* worker:
  * The `worker_thread` module is now stable
    #29512

PR-URL: #29695
BridgeAR added a commit that referenced this pull request Sep 25, 2019
Notable changes:

* crypto:
  * Add `oaepLabel` option #29489
* deps:
  * Update V8 to 7.7.299.11 #28918
    * More efficient memory handling
    * Stack trace serialization got faster
    * The `Intl.NumberFormat` API gained new functionality
    * For more information: https://v8.dev/blog/v8-release-77
* events:
  * Add support for `EventTarget` in `once`
    #29498
* fs:
  * Expose memory file mapping flag `UV_FS_O_FILEMAP`
    #29260
* inspector:
  * New API - `Session.connectToMainThread`
    #28870
* process:
  * Initial SourceMap support via `env.NODE_V8_COVERAGE`
    #28960
* stream:
  * Make `_write()` optional when `_writev()` is implemented
    #29639
* tls:
  * Add option to override signature algorithms
    #29598
* util:
  * Add `encodeInto` to `TextEncoder`
    #29524
* worker:
  * The `worker_thread` module is now stable
    #29512

PR-URL: #29695
BridgeAR added a commit that referenced this pull request Sep 25, 2019
Notable changes:

* crypto:
  * Add `oaepLabel` option #29489
* deps:
  * Update V8 to 7.7.299.11 #28918
    * More efficient memory handling
    * Stack trace serialization got faster
    * The `Intl.NumberFormat` API gained new functionality
    * For more information: https://v8.dev/blog/v8-release-77
* events:
  * Add support for `EventTarget` in `once`
    #29498
* fs:
  * Expose memory file mapping flag `UV_FS_O_FILEMAP`
    #29260
* inspector:
  * New API - `Session.connectToMainThread`
    #28870
* process:
  * Initial SourceMap support via `env.NODE_V8_COVERAGE`
    #28960
* stream:
  * Make `_write()` optional when `_writev()` is implemented
    #29639
* tls:
  * Add option to override signature algorithms
    #29598
* util:
  * Add `encodeInto` to `TextEncoder`
    #29524
* worker:
  * The `worker_thread` module is now stable
    #29512

PR-URL: #29695
@felixfbecker

This comment has been minimized.

Copy link
Contributor

commented Sep 26, 2019

Really excited to see more browser interop in Node ☺️

One thing that worries me is that this prevents once to listen to error for any kind of interop emitter that implements/extends both EventEmitter and EventTarget (and does so in a minor version).

I would have expected the check to be reversed: If the object is a Node EventEmitter, listen to error, otherwise not, but in the shipped version EventTarget is checked for first. I actually kind of would have hoped for Node's EventEmitterr to one day implement EventTarget for interop.

I also had a use case for error events on the web come to mind: When you open a WebSocket, you'll want to listen and wait for the open event to know when to start sending messages. If the connection fails during that wait (e.g. the server could not be reached) the error event will fire instead. You'll want to make sure these listeners are also cleaned up properly. The only difference is that it fires an Event object, not an Error object, which in my eyes is enough of an argument to not listen to it in once.

// EventEmitters, we do not listen to `error` events here.
emitter.addEventListener(
name,
(...args) => { resolve(args); },

This comment has been minimized.

Copy link
@domenic

domenic Sep 26, 2019

Member

EventTarget is guaranteed to only have one argument, so you could avoid passing an array here.

This comment has been minimized.

Copy link
@benjamingr

benjamingr Sep 28, 2019

Member

@domenic what do you think is the right call API wise in terms of compatibility with EventEmitter and the coming (?) events proposal?

This comment has been minimized.

Copy link
@domenic

domenic Sep 28, 2019

Member

I wouldn't pay much attention to early-stage TC39 proposals. But I think just compatibility with how events work in event listeners in the DOM would suggest using a single argument instead of an array containing that argument.

This comment has been minimized.

Copy link
@ronkorving

ronkorving Sep 30, 2019

Contributor

So this now resolves with an array, and just got released in Node v12.11.0. Are we now stuck with this promise signature?

This comment has been minimized.

Copy link
@addaleax

addaleax Sep 30, 2019

Member

So this now resolves with an array, and just got released in Node v12.11.0. Are we now stuck with this promise signature?

I think changing that would be a breaking change, yes.

That being said, I think there is some value in keeping this consistent between EventTarget and EventEmitter, so I’m not really sure that we should change this even if we could.

This comment has been minimized.

Copy link
@domenic

domenic Sep 30, 2019

Member

What about consistency with web EventTarget? If that is a goal, arrays should not get involved.

This comment has been minimized.

Copy link
@JeniaBR

JeniaBR Oct 1, 2019

Author Contributor

@domenic @addaleax I thought about return one argument, but for consistency with EventEmitter decide to keep the array.

I will open soon a new PR with some improvements.
We can go with EventTarget API and I will update the code, docs and the tests.

WDYT?

This comment has been minimized.

Copy link
@addaleax

addaleax Oct 1, 2019

Member

What about consistency with web EventTarget? If that is a goal, arrays should not get involved.

@domenic Yes, there’s a tradeoff, but at least to me a polymorphic API seems worse here than one that performs some unnecessary boxing.

That being said: I personally don’t like the decision to use arrays in the first place, but I think we’re stuck with that now.

@benjamingr

This comment has been minimized.

Copy link
Member

commented Sep 28, 2019

@felixfbecker that sounds very reasonable to me and was simply missed.

@JeniaBR would you be willing to put in the work to change the order so that the API checks if the passed in emitter is a Node EventEmitter first?

@JeniaBR

This comment has been minimized.

Copy link
Contributor Author

commented Oct 1, 2019

@felixfbecker good point!
@benjamingr sure, I will do it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.