This SO question & answers and DOM level3 docs states that manual events are dispatched synchronously in browsers. My question, however, relates to user-related events (real clicks), not manually triggered ones.
I created a small jsfiddle demo with a button + onclick handler, the handler does some synchronous work for 2 seconds (blocking sync wait, long enough for my eye to see what’s going on). Open console to see console.log
s.
The test. I click several times on the button. Although the first click (and its synchronous processing) makes the button pushed down (looks like disabled), other clicks are stored as well and processed later, asynchronously, via event loop.
The questions are:
- when does event dispatching exactly happen? Are user events (e.g. a click) immediately pushed to JS message queue or is there an intermediate Web API queue… or in other words – which queue is this event dispatching related to?
- what does it mean that most events (
click
,blur
, etc – except forload
) are processed synchronously in case of user events (not manual ones)? Given the test above, 1st click makes synchronous blocking callback executed (I expect nothing else to happen in the meantime) and, anyway, next clicks are stored in a queue. So storing events is parallel anyway (has to be, since main javascirpt thread is busy). Would there be any difference, theoretically, it events were processed asynchronously?
slightly modified demo shows that for a given click event, the event bubbles up and calls all related event handlers, like if event bubbling was blocking, until anything else (timeout in this example) can happen. But still it’s not clear why is event dispatching synchronous.
Advertisement
Answer
It makes little sense to speak of synchronous in the context of real events, since the event queue is only processed when the current execution stack has been emptied, i.e. when there is no more synchronous code to execute. That makes the related event handlers asynchronous. This is also true for the click event handler you provided in a fiddle.
That the running code blocks any other action while it runs is no indication of how that code was triggered: asynchronously or synchronously. In both cases a busy loop for 2 seconds will be blocking for 2 seconds. When we say code runs asynchronously, it only says something of how/when the code was invoked, not on how it runs. Both synchronous and asynchronous code run in a way that they are blocking. There are some exceptions to this — for instance web workers run in their own thread.
These “real” events are pushed on the JS message queue. Of course, they first are consumed from an OS event queue, but this is implementation specific. The important thing is that they end up in the JS message queue, together with a handler for them. The JavaScript engine only processes events when the previously running code ran to completion. By consequence these event handlers are called asynchronously.
In case of “manual” events, i.e. events triggered by code such as jQuery’s .trigger()
method, there is a sense in making the difference between asynchronous and synchronous. In the asynchronous case, the event would be put on the JS queue, and the current code would first run to completion. In the synchronous case, the event would not be put on the JS queue, but be executed like a function call. So the handling of the event happens as an additional call on the call stack, after which your code will resume, just like after the return from a normal function call: this is synchronous behaviour.
I ignore here the concept of micro tasks, which just means there are different priorities for executing asynchronous code.