6

So I answered a question recently and OP asked if I could add the following about DOM events to my answer:

Maybe you could also add to your answer that not only they are executed first, but the subsequent event is blocked until the first one finishes.

Well, can I add that? Do I know that with DOM events will run one event at a time and will wait for the previous one to finish before the next one starts?

Do I at least know this is always the case in browser JavaScript?

Finding a conclusive answer to this has been surprisingly difficult to be so far, I've expected a "yes" but I just can't find it.


Clarification: I'm not asking about adding other asynchronous handlers inside the handlers, or calling setTimeout or workers and such. All I'm asking is whether or not the order of event handler execution is guaranteed, and that the next one starts only the previous one finished executing ? A good answer would cite a credible source (preferably - a specification). Nothing about threading here.

Brian Tompsett - 汤莱恩
  • 5,195
  • 62
  • 50
  • 120
Benjamin Gruenbaum
  • 246,787
  • 79
  • 474
  • 476
  • depends on what the event handler does. if it defers a new script loading, that doesn't have to be finished before the next event handler bubbles up... even just a setTimeOut can hop the order. – dandavis Sep 16 '13 at 16:01
  • When you add a script it too will wait for it's turn to run. It doesn't break the event loop model. If you use a `setTimeout` you are intentionally manipulating the order. Keep in mind that the 2nd parameter to `setTimeout` is a _best effort_; if you pass `1000` there is no guarantee it will run exactly 1 second from now. It's common that you'll see it skew, especially over long periods. – Halcyon Sep 16 '13 at 16:03
  • Yes the browser is supposed to honor a single-threaded model within the JavaScript code. Web Workers is the only way to run code concurrently. However, I've found Firefox to have some bugs with this. If you are making **synchronous** ajax requests, and you are also listening to a websocket, Firefox will incorrectly call your websocket message handler when a message arrives even though your JavaScript code is currently blocked on the ajax request. Totally messes everything up when this happens. – Brandon Sep 16 '13 at 16:25
  • possible duplicate of [Is javascript guaranteed to be single-threaded?](http://stackoverflow.com/questions/2734025/is-javascript-guaranteed-to-be-single-threaded) – Bergi Sep 16 '13 at 16:34
  • @Bergi no, (and I actually referenece that question in the one I linked to). What I'm asking is - does the DOM API guarantee that event handlers are executed one after the other and the next one only after the last one completed? It's not about JS in general or even timers for that manner. It's about the DOM events api. – Benjamin Gruenbaum Sep 16 '13 at 21:27
  • @FritsvanCampen thanks for the attempt to help but that was not really the question. I'm asking about the DOM events themselves, not about adding more ajax withinn. Brandon, that's interesting, can you show a self contained code example that reproduces that behavior? (Is it a bug though? I can't find anywhere in the spec that says it's incorrect behavior and that really worries me) – Benjamin Gruenbaum Sep 16 '13 at 21:32
  • @BenjaminGruenbaum: I think the [event dispatch / callback invoke algorithm](http://www.w3.org/TR/dom/#concept-event-listener-invoke) suggests synchronous behaviour, but does not explicitly state it. Also I couldn't grasp what of this holds true when more than a single event is dispatched. – Bergi Sep 16 '13 at 21:55
  • If you accept the notion that JS is single threaded and that events are put into a queue, I don't understand how you would think that an event handler is not guaranteed to finish before another event handler is invoked. http://dev.opera.com/articles/view/timing-and-synchronization-in-javascript/ – Juan Mendes Sep 16 '13 at 22:04
  • @Bergi If two injected scripts finished loading at about the same time both firing the `load` event, do I know that the second handler will run only after the first handler has finished, it seems like common sense for this to happen (otherwise, what if they change the same variable at the same time for example?) but I find it very strange that I can't find this behavior specified anywhere. The dispatch/callback invoke algorithm is definitely an interesting place to start looking though :) Thanks, I have not considered it yet. – Benjamin Gruenbaum Sep 16 '13 at 22:05
  • Don't miss the link I put in, I'm really not getting how you think a JS method could be preempted – Juan Mendes Sep 16 '13 at 22:11
  • @JuanMendes: Nice article you found, but what about the "*Nested Events*" section? Sure, in general all asynchronous and independent events are put in the task queue. – Bergi Sep 16 '13 at 22:13
  • @Bergi Nested events will still work the same way, but yes, it's true that the state of the app may have changed if you have nested events, I did mention that in my answer – Juan Mendes Sep 16 '13 at 22:18
  • @BenjaminGruenbaum Don't go anywhere, there is actually a legitimate case where a race condition could happen, putting it into my answer – Juan Mendes Sep 16 '13 at 22:18

2 Answers2

6

Yes and no, all JS runs on the same thread, except for web workers, which don't have access to the DOM. http://dev.opera.com/articles/view/timing-and-synchronization-in-javascript/

Here's some relevant information on that page

All event handler functions are executed sequentially, and each event is processed completely (including bubbling up through the DOM and performing the default action), before the next event is processed.

However, later on that page, they mention

Race Conditions

Each window (and frame) has its own event queue. In Opera, every window has its own JavaScript thread. This includes windows in iframes. The consequence is that event handlers initiated from different frames might execute at the same time. If these simultaneous scripts modify shared data (like properties in the top window), we have the possibility of race conditions.

Events all are put into an event queue, and all event handling happens within that same thread. Along with all asynchronous callbacks, like XHR and setTimeout.

Beware of nested events also, since many methods will execute if you fire an event and they could change global state. Example http://jsfiddle.net/rpxZ4/

$('#d1').click(function(){
    alert('before ');
    $('#d2').trigger('click');
    $('#d3').trigger('click'); 
    alert('after ');
});

$('#d2, #d3').click(function() {
    alert('clicked ' +this.id);
});

Here are Opera's suggestions for dealing with timing

  • Don't have long-running scripts.
  • Don't use synchronous XMLHttpRequests.
  • Don't let scripts initiated from different frames manipulate the same global state.
  • Don't use alert boxes for debugging, as they might change the logic of the program completely.
Community
  • 1
  • 1
Juan Mendes
  • 80,964
  • 26
  • 138
  • 189
  • Where did "links coming..." go :(? Also, by synthetic event do you mean an untrusted event? – Benjamin Gruenbaum Sep 16 '13 at 21:41
  • Usually a synthetic event is untrusted, unless you're using a signed script with FF – Juan Mendes Sep 16 '13 at 21:49
  • Thanks for the answer. Still, this is an example where order is guaranteed but I wanted to know the general rule. When having two DOM events (like mousedown, load, mouseover, etc) , under what circumstance can I be _sure_ that the next one will fire after the last one has finished, and why? – Benjamin Gruenbaum Sep 16 '13 at 21:52
  • So you're asking... if a mousedown is guaranteed to occur before a click? and whether the blur event is supposed to happen before or after the click event? Or are you saying that my example doesn't prove that handlers can't get interrupted, that it's just an example? – Juan Mendes Sep 16 '13 at 21:53
  • I'm asking if generally, when having two arbitrary DOM events fire (let's say two `load` events for this example), is there a guarantee that the second handler will start firing only _after_ the first one has completed execution? – Benjamin Gruenbaum Sep 16 '13 at 21:57
  • Yes, my example in the answer shows that even in nested events that is true. I also explained that JS uses an event queue, not multiple threads for handling events, therefore, there's no way that a method could be preempted. If the JS thread is preempted, it's guaranteed to return to execution where it was, not in a different handler. – Juan Mendes Sep 16 '13 at 21:59
  • I am _truly_ thankful for your assistance but I still can't see how a single example with a specific event time (and without even using any DOM events explicitly but using jQuery) proves a general rule about how the event system works in the DOM API with JS. – Benjamin Gruenbaum Sep 16 '13 at 22:01
  • +1 Interesting concrete example of an example where a single JS environment this fails in practice. Interesting that they use kind of different terminology from the language spec. I'll read the article and see if they clear up things (although Opera run chromium now so implementation specific things might not be as helpful). – Benjamin Gruenbaum Sep 16 '13 at 22:28
  • 1
    @BenjaminGruenbaum The article is not implementation specific (like IE articles), it mentions other browsers in it all the time. – Juan Mendes Sep 16 '13 at 22:29
1

Well, that actually depends on what do you do in your handler and how do you define 1st handler ended moment?

For example, if you're doing only sync operations in eventHandler1 then you're sure that eventHandler2 won't get triggered before eventHandler1 finishes. the reason is javascript being single threaded.

But, imagine scenario like this: click on button1 triggers eventHandler1 which actually makes ajax request. in that scenario, what do you actually consider as 'end of eventHandler1'? if it is the moment when ajax request returns then certainly eventHandler2 will start (and possible end) execution before eventHandler1 finishes.

To put it short: whenever you do sync-only operations > the order is guaranteed.

Added from comments:

http://www.w3.org/TR/DOM-Level-3-Events/#sync-async for example, it says: " Each event in this virtual queue must be delayed until the previous event has completed its propagation behavior, or been canceled."


Well, now we're back to the question 'what type of events are we talking about'? As mentioned before: if it's async events then sure the order is not guaranteed. But the original dilemma was about click event and that one is not async but sync. And for sync events documentation clearly states: Events which are synchronous ("sync events") must be treated as if they are in a virtual queue in a first-in-first-out model, ordered by sequence of temporal occurrence, with respect to other events, to changes in the DOM, and to user interaction.


yep, no guarantees. now add javascript being single-threaded into play and you can't get them executing at the same time. but yes, speaking of DOM strictly - there is no guarantee whatsoever which one will happen before.


just one more comment... you might have exactly the same DOM but accessed from Java multi-thread environment. What then?:) then you have to implement your own thread-safe async events handling because you're no more 'protected' by single thread environment as you have with javascript. So, the conclusion, as i see it is that DOM specs does require that sync events are fifo implemented. for async events execution depends on stack/thread implementation. in Javascript, that means that 2 handlers can't overlap but in Java e.g. doesn't have to mean.

dee zg
  • 10,582
  • 7
  • 33
  • 59
  • I don't understand - why do you think the order is guaranteed? Where does it say so? I'm not asking about calling other asynchronous handlers in the handlers itself. – Benjamin Gruenbaum Sep 16 '13 at 21:28
  • I don't think it. http://www.w3.org/TR/DOM-Level-3-Events/#sync-async for example, it says: " Each event in this virtual queue must be delayed until the previous event has completed its propagation behavior, or been canceled." – dee zg Sep 16 '13 at 21:42
  • Yeah, that's the link I linked to in the question linked to this one. It would seem obvious that it would be there but I could not find it stating so anywhere (especially the use of multiple queues there is confusing to me), this might be a problem with me understanding the language well enough though, a specific quote or how you reached the conclusion would really help because I can't seem to be able to deduce this based on the spec. At least not for asynchronous events. – Benjamin Gruenbaum Sep 16 '13 at 21:46
  • Well, now we're back to the question 'what type of events are we talking about'? As mentioned before: if it's async events then sure the order is not guaranteed. But the original dilemma was about click event and that one is not async but sync. And for sync events documentation clearly states: _Events which are synchronous ("sync events") must be treated as if they are in a virtual queue in a first-in-first-out model, ordered by sequence of temporal occurrence, with respect to other events, to changes in the DOM, and to user interaction._ – dee zg Sep 16 '13 at 21:50
  • Interesting. So when using an event handler for `load` and using an event handler for `click` on another part of the code (or two load events) they can (while conforming to the spec) execute in the same time, or at least one can execute at the same time as the other, and before the other has finished? There is no guarantee they will execute one after the other? (I know this is very unlikely in _actual_ browser JS reading a few implementations) – Benjamin Gruenbaum Sep 16 '13 at 21:56
  • 1
    yep, no guarantees. now add javascript being single-threaded into play and you can't get them executing at the same time. but yes, speaking of DOM strictly - there is no guarantee whatsoever which one will happen before. – dee zg Sep 16 '13 at 22:06
  • Thanks, I've upvoted the answer as it has been useful. If you could add parts of the discussion in the comments to the answer itself that could be good for future visitors :) If nothing more decisive from the spec comes up I'll accept this answer. – Benjamin Gruenbaum Sep 16 '13 at 22:07
  • @JuanMendes JS itself does not define a notion of concurrency at all. It is up to _host objects_ that interact with JS. In this example it is the DOM Events API which is language agnostic but in practice used _mostly_ with JS. While I think it makes sense that events would run one after the other (that is, one handler finishing before the next one executing), and there _is_ a guarantee for that with events like _click_, it appears that there are events where this guarantee does not appear (at least in the specification) such as multiple `load` events, unless we're missing something here. – Benjamin Gruenbaum Sep 16 '13 at 22:16
  • just one more comment... you might have exactly the same DOM but accessed from Java multi-thread environment. What then?:) then you have to implement your own thread-safe async events handling because you're no more 'protected' by single thread environment as you have with javascript. So, the conclusion, as i see it is that DOM specs does require that sync events are fifo implemented. for async events execution depends on stack/thread implementation. in Javascript, that means that 2 handlers can't overlap but in Java e.g. doesn't have to mean. – dee zg Sep 16 '13 at 22:19
  • Please consider adding the contents of this (constructive) clarification to the actual answer by editing it http://i.imgur.com/UWPwQmU.png – Benjamin Gruenbaum Sep 16 '13 at 22:35