I would like to offload as much application logic into a Web Worker as possible. That includes logic that handles events. Since event propagation happens synchronously I cannot communicate with a Web Worker in order to determine if an event should be preventDefault-ed or stopPropagation-ed.
I think a solution could be to add waitUntil (or something similar) to MouseEvents.
anchor.addEventListener('click', ev => {
ev.waitUntil(new Promise((resolve) => {
let msg = Object.assign({}, ev);
worker.postMessage(msg);
worker.onmessage = ev => {
if(ev.data.preventPlease) {
ev.preventDefault();
}
resolve();
};
});
});
This also looks like a more elegant way to solve my previous concerns around using async code with the user-gesture requirement: User-gesture restrictions and async code
This basically breaks the way events work. You can’t just turn all synchronous checks for the canceled flag into asynchronous checks. That requires way too much changes.
Handling user input differently somehow might be doable, but someone should first write up how user input actually works today in more detail, since that is all still rather messy.
It’s true you can’t asynchronously cancel all events, e.g. a wheel event needs to know synchronously if it will block scrolling. However I don’t see why it wouldn’t work in general with non-cancellable events. The waitUntil method also helps make the user gesture requirements more reasonable.
I’m a bit concerned about the user experience of this. E.g. imagine a user clicking on a <span> inside a link and the link is actually followed like a minute later because the event hadn’t propagated from the <span> to the <a> until then.
I don’t really see how this would make that case worse than what is currently possible. For example, a site could be doing something like a sync xhr which could also block for a minute.
UX-sensitive actions like navigating the page could have a time limit imposed by the browser. For example the page must act on the event within 5 seconds, otherwise it loses the ability to perform the action.
Thanks everyone for comments! I agree that there are some possible UX concerns here. I’m not sure if this proposal is worse than the status quo, as a lot of this can be done using new Event.
Nevertheless, as a browser dev, @annevk, says this proposal isn’t possible I have to believe him and withdraw the idea. I am still very much interested in a way to handle events off the main thread; if there is another possible avenue here.
The reason for this is we have to asynchronously generate the data to copy, and so by the time the data to copy is ready, we no longer have permission to copy data to the clipboard, even if it only took milliseconds.
We have similar problems with popup windows. We really need a way to relax these restrictions.
anchor.addEventListener('click', ev => {
ev.preventDefault(); // prevent first
worker.postMessage({ ...ev });
worker.onmessage = ev => {
if (!ev.data.preventPlease) {
ev.doDefault(); // do the deferred default action
}
}
}
This problem becomes more pressing with OffscreenCanvas and the implications by browser vendors that heavy content like games ought to run in a worker.
Things like calling preventDefault() and performing user-gesture-restricted actions (which even include playing audio on some platforms) become effectively impossible if you have to postMessage() to a worker in an event handler.