-
Notifications
You must be signed in to change notification settings - Fork 147
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
RAII system for handling event listeners #30
Comments
Oh, also, similarly to Of course this should be used carefully, but sometimes you truly do want an event listener to last for the duration of your entire program. |
(Ignoring the potential higher-level API, and focusing on the actual proposal for now) I like this and think we should move forward with it! Nitpicks:
Do you have thoughts, @David-OConnor @alexcrichton @yoshuawuyts @ashleygwilliams? |
This all sounds great to me! I'd echo @fitzgen's points about |
Love it. If I understand correctly, this would tackle several issues:
Could use macros to populate the events. |
Good catch.
I agree, though I'm concerned about the ergonomics and flexibility of the API.
Shouldn't that be |
@David-OConnor To be clear, this proposal is just for As for a macro, yeah, that's exactly what stdweb does: it's literally 1 line of code to generate the |
One thing I would like to note: if So in that case my Do we want to discuss that now, or wait until we get more experience with using |
I thought of something: rather than putting a That still gives full flexibility, and avoids manual type casts, but it also avoids the |
So something like pub fn new<F, T>(target: &EventTarget, kind: &'a str, f: F) -> EventListener
where
F: FnMut(T),
T: JsCast,
{ ... } ? Would this do dynamically-checked or unchecked casts? I guess, as written above, it would have to be unchecked. I'm of two minds on this. On one hand, I'd also like if users weren't forced to use I guess I am leaning towards using
It should be cheaper at the ABI boundary to take a reference rather than ownership, and one can always |
And we can explore potential follow ups, like in your unresolved questions, that solve the casting problem in a better manner. |
Yes, exactly.
I was thinking dynamically-checked, with a separate
If we don't do automatic casts then that's a big hit to ergonomics, and makes things more error prone. Since event listening is so common, we need a good ergonomic solution. So if If we have consensus that something like
Woah, I didn't realize closure argument references behaved like that. I thought that only applied to regular functions. In that case that's a big 👍 from me. |
Coming in late here, but I really like this direction. Very excited for this! |
👍 Unless anyone would like to voice concerns, I think we are ready to
Did you want to tackle these @Pauan? Alternatively, we can write out some bullet points / skeleton and make it into a good first issue |
@fitzgen When I tried to implement this, I found out that @alexcrichton Is that intentional, or just an omission? If it's an omission, should I wait for reference support in wasm-bindgen before implementing this? |
WIP PR up: #42 Let's discuss some of the minor details there. |
High level design posted: #43 |
Oh oops sorry missed this! This is currently just an omission because it requires more (note that it currently requires a combinatorial explosion of impl blocks because these are distinct IIRC): impl<A, B> Trait for Fn(A, B)
impl<A, B> Trait for for<'a> Fn(&'a A, B)
impl<A, B> Trait for for<'b> Fn(A, &'b B)
impl<A, B> Trait for for<'a, 'b> Fn(&'a A, &'b B) |
@alexcrichton Does it? Can't it use a trait like this? trait WasmRefArgument {
// ...
}
impl<A> WasmRefArgument for A { ... }
impl<'a, A> WasmRefArgument for &'a A { ... } And then... impl<A, B> WasmClosure for Fn(A, B) where A: WasmRefArgument, B: WasmRefArgument { ... } Or does that not work? |
Hm perhaps! I basically just remember spending way too long getting it to work in the beginning and endlessly running into walls. I'm not entirely proud of the current state of things ( I think whatever works for supporting references is fine to implement as well, it should all be backwards compatible too! |
I think for the situation at hand, we can use owned versions of How does that sound? |
@fitzgen EDIT: Oops, I reread what you said, and I agree. 👍 |
Summary
I propose to implement an API which makes it easy to use Rust's RAII-based system for event listener registration/deregistration.
Motivation
When registering an event listener, it is often needed to deregister it later.
In JavaScript this is done with the
removeEventListener
method, but that is verbose and error prone. It also does not clean up Rust resources (e.g. the memory for the closure).Detailed Explanation
The idea is that you can simply call
EventListener::new(&node, "foo", move |e| { ... })
and when theEventListener
is dropped it will then cleanup the Rust memory for the closure and it will also deregister the event listener.This is a general purpose idiomatic mid-level API which can work with any event on any DOM node. I expect it to be used pervasively both within Gloo and outside of Gloo.
Drawbacks, Rationale, and Alternatives
For a mid-level API like this, there aren't really any alternatives. Some small things can be tweaked, and I haven't thought very hard about how to support
FnOnce
(but I'm sure it is possible). Ideas and refinements are welcome.Unresolved Questions
It is possible to build a higher level API on top of this.
This higher level API gives full static type support for JS events, making it impossible to get the types wrong.
The way that it works is that a new trait like this is created:
And a new
fn
is created, like this:Now you can create various events like this:
And now finally we can actually use the API:
This API has some huge benefits:
You no longer need to specify a string for the event type (so no more typos).
You no longer need to do any coercions to the correct type (that's all handled automatically now).
It guarantees that the static type is correct (in other words, if you have a
click
event, it is guaranteed to be matched to aClickEvent
Rust type, and vice versa).This technique was pioneered by stdweb, and it works out really well in practice. It may also be necessary for solving rustwasm/wasm-bindgen#1348
But that's something which can be layered on top of the mid-level
EventListener
API, so I defer it to later.The text was updated successfully, but these errors were encountered: