garronej / evt

💧EventEmitter's typesafe replacement
https://evt.land
MIT License
454 stars 6 forks source link

EventEmitter to evt sample. #32

Open UrielCh opened 2 years ago

UrielCh commented 2 years ago

Hi,

can you provide conversion list from EventEmitter to Evt ?

should we extend Evt in ours class ? is this.emit(type, parma) should be replace by a this.evt.push([type, param]) is obj.on('event', (params) => {do stuff}) become evtText.attach??? is obj.once('event', (params) => {do stuff}) become evtText.attachOnce??? and where is the detach ?

garronej commented 2 years ago

Hi @UrielCh ,

should we extend Evt in ours class

You can if you want, here is the doc: https://docs.evt.land/extending_evt

is this.emit(type, parma) should be replace by a this.evt.push([type, param])

It depends,
If you want to have only one Evt instance that is a single bus for all your event type (like with EventEmitter) you can do:

import { Evt } from "evt";

//Equivalent of const eeSocket = new EventEmitter();
const evtSocket = Evt.create<
    | { type: "connect"; } 
    | { type: "disconnect"; data: { cause: "remote" | "local" }; }
    | { type: "error"; data: Error }
>();

//Equivalent of eeSocket.emit("disconnect", { "cause": "local" });
evtSocket.post({ "type": "disconnect", "data": { "cause": "local" });

//Equivalent of eeSocket.on("disconnect", data => { /* ... */ });
evtSocket.$attach(
       event => event.type !== "disconnect" ? [ event.data ] : null,
       data => { /*...*/ }
); 

//Equivalent of eeSocket.once("disconnect", data => { /* ... */ });
evtSocket.$attachOnce(
       event => event.type !== "disconnect" ? [ event.data ] : null,
       data => { /*...*/ }
); 

Or, if you think the above example is too verbose:

import { Evt, to } from "evt";

//Equivalent of const eeSocket = new EventEmitter();
const evtSocket = Evt.create<
    | [ "connect", void ]
    | [ "disconnect", { cause: "remote" | "local" } ]
    | ["error", Error ]
>();

//Equivalent of eeSocket.emit("disconnect", { "cause": "local" });
evtSocket.post([ "disconnect", { "cause": "local" }]);

//Equivalent of eeSocket.on("disconnect", data => { /* ... */ });
evtSocket.$attach(
       to("disconnect"),
       data => { /*...*/ }
); 

//Equivalent of eeSocket.once("disconnect", data => { /* ... */ });
evtSocket.$attachOnce(
       to("disconnect"),
       data => { /*...*/ }
); 

But Evt really shines when you have one instance of Evt by event type:


const evtConnect = Evt.create();
const evtDisconnect= Evt.create<{ cause: "local" | "remote"; }>();
const evtError = Evt.create<Error>();

//Equivalent of eeSocket.on("disconnect", data => { /* ... */ });
evtDisconnect.attach(
       data => { /*...*/ }
); 

//Equivalent of eeSocket.once("disconnect", data => { /* ... */ });
evtDisconnect.attachOnce(
       data => { /*...*/ }
); 

where is the detach

If you want a direct equivalent you can refer to this section of the doc

However, the recommended way to detach handlers is to use Ctx: example:

import { Evt } from "evt";

const ctx = Evt.newCtx();

evtSocket.$attach(
       event => event.type !== "disconnect" ? [ event.data ] : null,
       ctx,
       data => { /*...*/ }
); 

//Equivalent of eeSocket.once("disconnect", data => { /* ... */ });
evtSocket.$attachOnce(
       event => event.type !== "disconnect" ? [ event.data ] : null,
       ctx,
       data => { /*...*/ }
); 

// Detaches all handlers attached using ctx.
ctx.done();
evtSocket.$attach(
       to("disconnect"),
      ctx,
       data => { /*...*/ }
); 

evtSocket.$attachOnce(
       to("disconnect"),
       ctx,
       data => { /*...*/ }
); 

evtDisconnect.attach(
      ctx,
      data => { /*...*/ }
); 

evtDisconnect.attachOnce(
       ctx,
       data => { /*...*/ }
); 

Hope it helps.

You can have a look at the playground: https://stackblitz.com/edit/evt-playground?embed=1&file=index.ts&hideExplorer=1

UrielCh commented 2 years ago

in my case I have a single event bus per class.

so with evt, evtSocket.$attach will receves all event type, and will have to filter the correct type ?

for the event definition I'm already to my 3th implementation, the first one was an:

interface {
  on("event1", (params: type) => void),
  off("event1", (params: type) => void),
  once("event1", (params: type) => void),
  on("event2", (params: type) => void),
  off("event2", (params: type) => void),
  once("event2", (params: type) => void),
}

the second one: see

export interface ProtocolEventsApi {
    "Accessibility.loadComplete" (params: Protocol.Accessibility.LoadCompleteEvent, sessionId?: string): void;
    "Accessibility.nodesUpdated" (params: Protocol.Accessibility.NodesUpdatedEvent, sessionId?: string): void;
    "Animation.animationCanceled" (params: Protocol.Animation.AnimationCanceledEvent, sessionId?: string): void;
...
}

the 3th one see:

export type ProtocolEventsApi = {
  "Accessibility.loadComplete": [ params: Protocol.Accessibility.LoadCompleteEvent, sessionId?: string ];
  "Accessibility.nodesUpdated": [ params: Protocol.Accessibility.NodesUpdatedEvent, sessionId?: string ];
  "Animation.animationCanceled": [ params: Protocol.Animation.AnimationCanceledEvent, sessionId?: string ];

And now I should change to a 4th... that will be somthink like:

export type ProtocolEventsApi  =
  | { type: "Accessibility.loadComplete"; data: [ params: Protocol.Accessibility.LoadCompleteEvent, sessionId?: string ] } 
  | { type: "Accessibility.nodesUpdated"; data:  [ params: Protocol.Accessibility.NodesUpdatedEvent, sessionId?: string ] }
  | { type: "Animation.animationCanceled"; data:  [ params: Protocol.Animation.AnimationCanceledEvent, sessionId?: string ] }

or

export type ProtocolEventsApi  =
  | { type: "Accessibility.loadComplete"; data: { params: Protocol.Accessibility.LoadCompleteEvent, sessionId?: string } }
  | { type: "Accessibility.nodesUpdated"; data:  { params: Protocol.Accessibility.NodesUpdatedEvent, sessionId?: string } }
  | { type: "Animation.animationCanceled"; data:  { params: Protocol.Animation.AnimationCanceledEvent, sessionId?: string } }

or

export type ProtocolEventsApi  =
  | { type: "Accessibility.loadComplete"; data: Protocol.Accessibility.LoadCompleteEvent, data2: string }
  | { type: "Accessibility.nodesUpdated"; data: Protocol.Accessibility.NodesUpdatedEvent, data2: string }
  | { type: "Animation.animationCanceled"; data:  Protocol.Animation.AnimationCanceledEvent, data2: string }

Switching from the 1st to the second, was ok, it removed the needs to duplicate all on, one, off, addlistener.... Switching from the 2nd to the 3th, removed the boilerplate :void

but switching to your implementation, mean adding 3 times mode boilerplate

And you should check http://semasim.com which is different from http://www.semasim.com...

et puis entre francophone dans le Typescript et la telephonie, on devrait pouvoir trouvez des interets commun.

UrielCh commented 2 years ago

in this page the syntax is more compact

export type ProtocolEventsApi  =
  | [ "Accessibility.loadComplete", Protocol.Accessibility.LoadCompleteEvent, string ]
  | [ "Accessibility.nodesUpdated", Protocol.Accessibility.NodesUpdatedEvent, string ]
  | [ "Animation.animationCanceled", Protocol.Animation.AnimationCanceledEvent, string ]

like this, the type declaration have the same size, but I'm not able to use this structure to type my params.

I can not do something like

function doNodesUpdated(typeof ProtocolEventsApi["Accessibility.nodesUpdated"]) {
// code
}
garronej commented 2 years ago

Hi @UrielCh,

It would look something like this:

type ExtractArgs<key extends ProtocolEventsApi[0]> = [
    Extract< ProtocolEventsApi,[key, any, any]>[1],
    Extract< ProtocolEventsApi,[key, any, any]>[2],
];

function doNodesUpdated(...args: ExtractArgs<"Accessibility.nodesUpdated">) {
    // code
}

And you should check http://semasim.com/ which is different from http://www.semasim.com/...

Thanks for reporting this! Where did you find the faulty URL?

UrielCh commented 2 years ago

There is no faulty URL, but do not expect the URL given from a presentation not to be shortened by part of the audience.

nice ExtractArgs.

So this tuple approach is valid.

and since I do not want do make to mush change to the nodejs version, I will proxy the evt logic in some

Can you give me a project that use evt in deno and denoify to release a npm flavor ?

garronej commented 2 years ago

There is no faulty URL, but do not expect the URL given from a presentation not to be shortened by part of the audience.

Ah yes I see! Thanks for bringing it up!

Can you give me a project that use evt in deno and denoify to release a npm flavor ?

Denoify is for transpiling node to deno, not the other way around.
If you are writing your code for Deno, denoify won't let you publish on NPM.

The simplest example, that I use myself as reference whenever I want to publish on both NPM and Deno.land/x is tsafe.
You can copy paste the CI setup, it's completely portable.

You can also start from the setup of EVT itself. Note that EVT has three dependencies:

image

But they are transparently replaced by their deno build while transpiling because tsafe, run-exclusive and minimall-polyfill are all denoified packages:

image

This means that if your project uses EVT you'll just have to run npx denoify and voilà, everything will be transpiled sucesfully. Denoify will be able to find the deno distribution of EVT itself.

Now if your project have other dependencies, in this repo I sums up the differents approach you can implement to deal with dependencies that are not automatically denoifiable.

garronej commented 2 years ago

FYI @UrielCh, I updated the EventEmitter to Evt migration guide: https://docs.evt.land/migrating_from_events

UrielCh commented 2 years ago

Hi,

My migration is on hold due to issue

node_deno_shims looks not very active.