Re: [whatwg/dom] Proposal: asynchronous event listeners (Issue #1308)

This was discussed at the TPAC breakout discussion on 2024-09-25:

<details><summary>The full IRC log of that discussion</summary>
flackr: mmocny: this is a discussion around ideas for 5 areas worthy of discussing. The bulk of the conversation could be for the first proposal but I wanted to seed the rest <br>
noamh joined the room<br>
ayu joined the room<br>
flackr: mmocny: [reads from slide]<br>
aykut joined the room<br>
flackr: mmocny: There's a large lack of support for passive listeners. When some action happens, all event listeners are immediately dispatched<br>
flackr: mmocny: When I click button, the counter increases value and does some expensive work<br>
flackr: mmocny: My component isn't bad, something else is blocking the update<br>
smaug joined the room<br>
flackr: mmocny: Developers often wish to respond to events at some lower priority without blocking paint. There's currently no way to signal to the platform. There's many patterns (e.g. double raf) to try to do this<br>
mustaq joined the room<br>
flackr: mmocny: Many listeners don't need to do any work to implement the default action<br>
flackr: mmocny: common example, cookie consent dialogs have many things that follow the click, e.g. dialog disappears, records choice. There's a case study where one cookie response provider saw an improvement of ??% by doing this<br>
flackr: mmocny: another idea is passive events. click events only dispatch non-passively even if you add a passive listener today. But if you polyfill this, you can delay expensive work<br>
flackr: mmocny: Some differences, passive events can't preventDefault. It can be easy to polyfill, but relying on polyfills makes it less accessible and less used in practice. Native impls might have some opportunities to improve perf. We might also have an opportunity for interventions, e.g. some script is only allowed to listen passively<br>
flackr: mmocny: an extension is to pass a priority. Right now, the priority is implicitly very high but if the dev knows priority is low maybe the dev could tell the UA<br>
flackr: mmocny: some apis have noticed this and work around it, e.g. beforetoggle and toggle for popover<br>
nathan joined the room<br>
flackr: mmocny: beyond UI events, there are other apis like IntersectionObserver which are inherently async, perhaps others could be<br>
flackr: bas: why passive? why not async?<br>
flackr: mmocny: any name you want. I picked this because passive exists<br>
flackr: mmocny: this was a prior choice made for scrolling, to allow devs to declare they don't need to prevent the event<br>
flackr: dom: since it's more about the effect that caused the event, that's why passive was chosen whereas async is ambiguous (microtask)<br>
flackr: mmocny: there are risks with deferring work because of unloads. You could attach listeners for every link click, you could block navigation by adding expensive work on click<br>
flackr: mmocny: worse yet, it prevents the start of any network request, speculation rules address some of this, but the issue is that at any point you could prevent the nav from happening<br>
flackr: mmocny: a nice script might choose to yield or add a passive listener to avoid this<br>
flackr: mmocny: however, once you do this, if the document unloads there's a very limited time to run your script, the effect could get dropped<br>
flackr: mmocny: [shows demo of this]<br>
flackr: mmocny: we know from lots of people that tried to be nice and yield they saw a decreased reliability and went back to blocking the nav<br>
noamh left the room: Quit: noamh<br>
flackr: mmocny: maybe we need assurances that tasks are flushed before unload. If you task could already have blocked the nav, and you choose to yield, maybe we can ensure we run them before unload<br>
flackr: mmocny: there is a pattern, idleUntilUrgent, that does this using doc lifecycle events<br>
flackr: mmocny: scott presented an api which could possibly have something like runBeforeUnload<br>
flackr: dom: with idleUntilUrgent, are we guaranteeing the task will run? If the task takes forever or there's 200 tasks before it we want to guarantee it succeeds/<br>
flackr: mmocny: correct, these scripts are currently guaranteed to run, we'd like to give them a lesser guarantee but some assurance that we will try to run them<br>
flackr: mmocny: there are reasons to block click events that might still be there, but most could likely do the nice thing<br>
flackr: dom: is there any diff between please before unload and having event handler run immediately and schedule the action in the unload listener?<br>
flackr: mmocny: not sure, there are reasons we motivate people not to do things before unload, like prevents bfcache, this could also run before unload<br>
flackr: dom: so it's like please run when idle or at worst before unload<br>
flackr: mmocny: right, it lets you be low priority before unload, then guaranteeing<br>
flackr: mmocny: there is a desire to track a string of async events. some sites create concepts to track groups of work<br>
flackr: mmocny: support for passive events might add to this problem, you might have many actions arbitrarily scheduled<br>
flackr: mmocny: maybe we should support tracking completion of effect<br>
flackr: mmocny: developers have asked for this, state of event listener dispatch, etc<br>
flackr: mmocny: there's also a string of projects to track scheduled work, task attribution, aync ??<br>
flackr: mmocny: we could have a TaskController, Finalize Registry<br>
flackr: mmocny: some folks have asked for preventDefault support. Right now you have to decide in the synchronous event whether to preventDefault<br>
flackr: mmocny: this can be difficult, right now you have to preventDefault always and then recreate the default action<br>
flackr: mmocny: some libraries debounce all events, with the real listeners delayed by one anim frame<br>
flackr: mmocny: validation could require a network hop<br>
flackr: mmocny: [reads from slide]<br>
flackr: mmocny: one common thing, you click, await fetch, before updating events<br>
flackr: mmocny: document loading, there's a moment in time where the page is loaded and painted but not ready to receive input yet<br>
flackr: mmocny: we have blocking=render but we don't have blocking=interactions<br>
flackr: mmocny: it is very recommended that sites server render content without requiring scripts to run, but this means early renderings are without event listeners on many sites<br>
flackr: .. very common to wait for DOMContentLoaded which means whole body has finished<br>
flackr: .. module scripts are implicitly after this<br>
flackr: .. many developers report that early interactions seem to perform better, but it's doing nothing<br>
flackr: .. some workarounds, inline JS in the HTML, have a wrapper to capture and replay listeners later<br>
flackr: ... more recently, there's a clever use of forms where all actions are wrapped with a form. But when your page is loading if an event arrives we'll do the default thing and submit the form reloading the page. It would have been much faster to run the already loaded script<br>
flackr: .. maybe we shoudl be delaying event dispatch<br>
flackr: .. if your script that would register event listeners is ready maybe we should let it run and put it behind the event queue<br>
flackr: ... similar question, what about hit testing? we should capture before the layout shift<br>
flackr: .. 5. lazy listeners. There could be many targets on the page that are lazy in loading controllers or listeners<br>
tbondwilkinson joined the room<br>
flackr: .. we complain we're shipping too much JS, we incentivize being ready for event dispatch. Many sites rely on event capture and replay<br>
flackr: ... instead of creating a giant bundle, server render the page, get started preloading portions of page (at low priority)<br>
flackr: ... then when user does interact, switch to priority loading for that listener<br>
flackr: .. but you have to rely on event replaying<br>
flackr: .. this can be complicated, but it also breaks a lot of UA default features, preventDefault, user activation, accessiblity, etc<br>
flackr: .. Maybe addEventListener could support promises<br>
flackr: .. possibly following service worker event.waitUntil pattern<br>
flackr: .. nav api has event.intercept<br>
flackr: .. view transitions returns a promise, waits until resolved<br>
flackr: .. or what if onclick could have a URL or some pattern to know what to load<br>
flackr: q?<br>
* Zakim sees no one on the speaker queue<br>
noamr: q+<br>
* Zakim sees noamr on the speaker queue<br>
domfarolino joined the room<br>
flackr: noamr: about waitUntilUrgent, is this mostly common in listeners that are the reason for the notification. e.g. click link that is navigating, and you yield in there? Or is it a more general problem?<br>
flackr: bas: a save button could be the same thing<br>
flackr: noamr: right, if you save there's a low chance you're navigating<br>
flackr: bas: save and quit is common<br>
noamh joined the room<br>
flackr: mmocny: I think it is general, unique thing is when you're interacting with event that navigates you could have blocked it<br>
flackr: ack noamh <br>
* Zakim sees noamr on the speaker queue<br>
flackr:  noamh: we started working on fetchLater for last minute fetching on unload. Maybe we need to think about this more hollistically instead of pushing more arbitrary code towards unload<br>
flackr: s/noamh/noamr<br>
flackr: mmocny: FYI fechLater is an API that can fetch even after unloading. The two features would work really well together. E.g. you want to stuff the fetch payload eagerly, so that it is queued. It's the second part of the equation<br>
flackr: noamr: can the first part also be more general?<br>
guohuideng left the room: Quit: guohuideng<br>
flackr: mmocny: I'm motivated to solve this as lack of passive event listeners is significantly impacting web perf<br>
flackr: mmocny: and the hesitation to do so would be addressed, at least partially, by this<br>
flackr: noamr: one thing comes to mind, if we had passive listeners, and there is a nav starting. The nav starting would be a sign to run the listeners, or some, or multiple levels e.g. i don't care if it gets dropped<br>
flackr: noamr: something a bit more generic than last minute work for the scheduler<br>
flackr: bas: concrete example<br>
flackr: noamr: if you have an onclick that adds something to analytics. This click event won't affect input feedback in any way, doesn't need to be quick just needs to happen before unload. You also don't want to block navigation and make it slower<br>
flackr: bas: Maybe i misunderstood, but idleUntilUrgent also doesn't block<br>
flackr: noamr: right, you don't want it to block, but it should block commit<br>
flackr: mmocny: my undersatnding is we unload the doc but allow event loop to continue<br>
flackr: mmocny: there are certain features that stop working but not all. I don't know if you have to delay this or not, but right now page freezes and you continue to do things<br>
flackr: bas: wouldn't stuff stop working?<br>
flackr: mmocny: in my experimentation, we'll commit the nav, show the new doc, but in the background there's some amount of time to schedule tasks.<br>
flackr: bas: but those tasks might expect stuff to be around, like document<br>
flackr: noamh: how critical is it to have reliably running events low priority?<br>
flackr: mmocny: i believe the priority is distinct from desire to run before unload<br>
flackr: mmocny: when dev says it's okay to run on idle, it means user is not waiting<br>
flackr: bas: i agree, can we move away from idleBeforeUrgent which implies priority<br>
flackr: bas: run before unload is more obvious to me<br>
flackr: mmocny: maybe it's priority: eventually or priority: background *and* run before unload<br>
flackr: bas: that makes more sense to me<br>
flackr: mmocny: we could consider whether all tasks should be flushed. We could make it easier but we could choose to be more efficient<br>
flackr: noamh: i still want to challenge assumption. Do we really want to encourage building something that assumes tasks will block navigation or might run while nav is happening? It seems like the wrong guidance<br>
flackr: noamh: but ... there's no alternative for users, e.g. say they want to do a critical activity<br>
flackr: bas: save the doc?<br>
flackr: noamh: but that should be part of building a reliable application<br>
flackr: noamh: you might want a more persistent way of doing things, not necessarily execute JS but have some ?? you can store<br>
flackr: noamh: we can never guarantee<br>
flackr: bas: there's never a guarantee anyways<br>
flackr: noamh: exactly, developer needs to consider this<br>
flackr: bas: right, but the probability is very different<br>
flackr: bas: you develop your app on the assumption it won't crash, so you don't have to auto-save often, and good probability you don't lose stuff. If we can increase the odds that we can run before unload it helps<br>
flackr: dom: I'm all for using priority based vocab to refer to increasing the probability that your work will run<br>
flackr: dom: i'm not sure about unload. I'm worried we'll push lots of work towards unload<br>
flackr: dom: I'm worried we'll have apps be fast until they unload<br>
flackr: dom: I think we should talk about it in terms of priority. At what point in time can you tell the platform it is urgent to do your work rather than assuming the last point in time<br>
flackr: bas: like an idle timeout?<br>
flackr: mmocny: these are all great points. There's an implicit deadline that when you run code that blocks the user interaction there's an implicit frustration. We're moving this to the background<br>
flackr: mmocny: I understand the concern, it is about signaling some assurance / making the patterns easier. The alternatives are yield and hope for best, but folks choose not to do this today<br>
flackr: mmocny: alternately you can guarantee it runs and reduce perf, and folks choose to do this<br>
flackr: mmocny: they are incentivized to do the bad thing<br>
flackr: bas: I'd like to understand concern, what if we accumulate huge amount of things, why would that happen?<br>
flackr: dom: probably lots of tasks would happen, but more would accumulate right before unload than would today<br>
flackr: Scott: we are encouraging putting things in pagehide, but yes does making it easier become dangerous?<br>
flackr: mmocny: maybe unload is wrong, e.g. resource contention, time limit may not be enough. Maybe this is high priority work. It's not supposed to block the interaction, but maybe it's eagerly flushed<br>
flackr: noamr: I see this work as after input feedback before navigation response. It doesn't have to block nav start, etc<br>
flackr: bas: there's multiple use cases<br>
flackr: * everyone agrees lower than feedback<br>
flackr: ??: i've looked at numbers, the unload event typically fires around the same as response time. we're not blocking navigation start<br>
flackr: mmocny: right, you can do this already<br>
flackr: philip: another scary situation, when doc starts unloading before it completes loading. We want to collect information. Do we want to make sure the nav still gets blocked?<br>
flackr: mmocny: I see some folks deploying large apps in room, which are serious problems as opposed to problems that you've solved and are fine<br>
flackr: mmocny: personally, I see it all the time<br>
flackr: bas: is it possible frameworks frequently hide this from people?<br>
flackr: mmocny: how?<br>
flackr: bas: don't know<br>
flackr: mmocny: stories i've heard is an ecommerce site invested in SSR, but it takes seconds for page to be interactive.<br>
flackr: mmocny: this can be busy loading script, or browser is optimizing first render and hasn't attempted to load script yet, event is immediately handled as a no-op<br>
tbondwilkinson: q+<br>
* Zakim sees noamr, tbondwilkinson on the speaker queue<br>
flackr: mmocny: this might trigger default action, but it doesn't do anything<br>
flackr: smaug: inert attribute, should you inert the html element?<br>
flackr: bas: but it blocks the interaction, doesn't store it<br>
flackr: smaug: but we can't replay it<br>
flackr: .. layout is different<br>
nathan left the room: Quit: nathan<br>
flackr: mmocny: my theory is this is already racy. When i interact, i send event to OS > browser > renderer > schedules, etc, it might not hit test for many frames, i hope there's no layout shift that changes target<br>
flackr: mmocny: I still think we should find the visible target<br>
flackr: mmocny: but we could do the same hit test we do today, capture the target immediately, but dispatch later<br>
flackr: mmocny: you might have a normal priority task which would have added the event listener<br>
flackr: smaug: of course you don't know the dependencies<br>
flackr: bas: the author can indicate it in this case<br>
flackr: smaug: the script might just be a tracking script. You just want to run the user input before the tracking script. It could be hard to figure out which scripts you want to load<br>
flackr: mmocny: right, that's a risk<br>
flackr: Ack noamr<br>
* Zakim sees tbondwilkinson on the speaker queue<br>
flackr: Ack tbondwilkinson <br>
* Zakim sees no one on the speaker queue<br>
noamh: q+<br>
* Zakim sees noamh on the speaker queue<br>
smaug: (we're running quite a bit overtime)<br>
flackr: tbondwilkinson: progressive hydration is pretty buggy. angular is working on it, others. That's one thing that may be interesting to focus on since frameworks are focusing on this. None of these seem like a complete dud. As a thought, how many frameworks use browser's event dispatch system?<br>
flackr: .. probably not many, because it's not a great system<br>
flackr: .. anything we can do to improve this would be great to encourage people to use the system<br>
Scott left the room: Quit: Scott<br>
domfarolino left the room: Quit: domfarolino<br>
noamr left the room: Quit: noamr<br>
noamh left the room<br>
aykut left the room: Quit: aykut<br>
flackr: mmocny: thanks for discussion. I've linked to all of this from the session<br>
</details>

-- 
Reply to this email directly or view it on GitHub:
https://github.com/whatwg/dom/issues/1308#issuecomment-2377284078
You are receiving this because you are subscribed to this thread.

Message ID: <whatwg/dom/issues/1308/2377284078@github.com>

Received on Thursday, 26 September 2024 15:25:12 UTC