So, basically, I want to do is:
- Get an Event from an event listener,
- handling that event,
- waiting for it to finish,
- and then pass to another in the queue,
so if I get like 15 an event 15 times, I don’t want to be handled all at the same time but more like event handle-await seconds event. I’ve tried like this to have one printed, then 5 seconds, then two with multiple clicks but what I’ve got was a lot of "one" at the same time and then a cascade of "two."
document.addEventListener("click", async function(event) {
if (!event.target.matches(".button-name"));
console.log("one");
await sleep(5000);
console.log("two");
event.preventDefault();
}, );
async function sleep(milliseconds) {
return new Promise((resolve) = > setTimeout(resolve, milliseconds));
}
3
Answers
To fix this problem, you need to use an async generator function that yields each click event as it occurs.
For example:
You can learn more about how JavaScript handles events and callbacks using the event loop and the queue from these sources:
A fairly simple solution would be to have a variable hold a promise. We can then use this promise as a queue. Whenever the event handler is called, we can chain a new task to this queue.
There are a few important things to note here.
The above doesn’t handle exceptions. Whenever one of the queued tasks fails the entire queue will stop. To prevent this make sure each added task has its own catch mechanism.
event.preventDefault()
can only be called as direct response to an event. If you call it in an queued function, the window of opportunity is gone so it will not have any meaningful effect.In the snippet above
queue
is visible to other code within the same scope. If you do not want this you can wrap the above handler in an IIFE.Why it doesn’t work.
When you execute an
await
operator, the execution context is copied and saved in heap memory, and execution of the next lower JavaScript execution context on the call stack is resumed, or if there is none, control returns to the event loop.If
await
is used in an event handler, the handler is called via the event loop and control returns to the event loop immediately when anawait
operator is executed. This allows more events to occur, which is why you get multiple "ones" from events as they occur, and then a lot of "twos" as the sleep promises become settled after a delay.You could put data from Event objects into a queue, or possibly event objects themselves1, and process them asynchronously after being decoupled from the event processing model. Some design and experimentation would be called for if events have default actions.
Depending on the aim of the code there may be alternatives to the queue as well: if throttling events is the goal, meaning to ignore new events if a previous one of the same kind is being processed, then some state based processing as opposed to a queue could be used. As always, a final design would depend on real application requirements.
1 Never use
window.event
in event handlers. It comes from the design of Internet Explorer and Microsoft’s JScript architecture. It can be overwritten if events occur while an event handler is awaiting something during asynchronous code execution.