Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript events fire in wrong order?

Can someone explain to me why when running the program below alerts I get is always "aaa" followed by "bbb"? I would expect it to follow steps:

  1. I start the program

  2. program runs and gets to setTimeout line. It sets timer and this timer will be fired in 5 seconds from this very point in time (not when the program finishes). There is nothing put in the event queue just yet

  3. then, before 5 seconds is passed I click the document. As program is busy and still running the loop it will put callback for this click event in the event queue. This is the only event in even queue at the moment (timer set earlier had not fired yet)

  4. 5 seconds is past, loop probably is still running and timer we set with setTimeout fires. This puts timers callback "aaa()" into event queue as second event callback to be executed, right behind click event that is already in a queue.

  5. So when program finishes I would expect it to alert "bbb" first as this event was fired first (click before 5 seconds), then followed by "aaa" (setTimeout event fire), I always get however "aaa" followed by "bbb", why is that?

Isn't event queue First in FIFO (First in first out)? Is it browser dependent? WHy is this happening?

function aaa() {
    alert("aaa");
}

setTimeout(aaa, 5000);

document.onclick = function () {
    alert("bbb");
}

for (i = 0; i < 1000000; i++) {
    console.log(i);
}

JSFiddle is here http://jsfiddle.net/sQvYG/1/

EDIT: Unless I'm missing something (possibly very embarassing) it looks like the code above does very similar thing to what John Resigs blog posts explains in details at http://ejohn.org/blog/how-javascript-timers-work/. Exactly the same thing, timer starts, click occurs and is added to the queue. Timer fires and is added to the queue. Execution ends and click handler is executed first and thats what I would expect. Anyone can shed some light on why this is not happening in the right order for me?

EDIT: thanks to bfavaretto who delivered accepted answer and was kind enough to explain queues in details I came to understanding that there is no one, but multiple "task queues" where events are placed. So the 5 steps I initially described above are in fact these:

STEP 1. (this is the same) I start the program

STEP 2. (this is the same) program runs and gets to setTimeout line. It sets timer and this timer will be fired in 5 seconds from this very point in time (not when the program finishes). There is nothing put in the event queue just yet.

Also I believe at this point nothing is placed in tasks queue just yet as I found on mozilla dev page https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/EventLoop this:

"Calling setTimeout will add a message to the queue after the time passed as second argument."

I also verified this in Chrome and it seems that callback is added to tasks queue when its actually fired.

STEP 3. then, before 5 seconds is passed I click the document. As program is busy and still running the loop it will put callback for this click event in one of the task queues that browser provides for this sort of event (mouse click, ui task queue). This is the only event in this task queue at the moment. Other tasks queues e.g. for timeouts are empty at the moment.

STEP 4. 5 seconds is past, loop probably is still running and timer we set with setTimeout fires. This puts timers callback "aaa()" into task queue provided by a browser specifically for this sort of events (timeouts). So now we have one task queue dealing with user interactions and we have one callback in there waiting (the callback from our click earlier in point 3). And we have another task queue that deals with timeouts and we have one callback in there waiting as well. That's the one with aaa() method that just fired after 5 seconds.

STEP 5. So when program finishes we have two task queues with one callback each. Now even though click was put into its tasks queue first there is no guarantee whatsoever that this task queue (which is user interaction or something that deals with mouse clicks) will be processed first. This totally depends on browser implementation and that's what was giving me headaches until bfavaretto explained it in his answer.

So in this example I mentioned only two task queues, browser however might have others. When program runs and is busy each type of event is placed in task queue for its type. After program finishes browser decides which tasks queue to process, grabs callback from there, executes, once finished grabs something from the same or another tasks ques and so on. So in fact there is no way of knowing which queue will be processed next. Tasks in each particular queue are always processed in order however.

Ooff.. this seems to make sense now, Resig's article gave me overall idea of how things are done but as it turned out there is much more to it and hopefully finally I understand it right.

like image 780
spirytus Avatar asked Jan 27 '26 13:01

spirytus


1 Answers

program runs and gets to setTimeout line. It sets timer and this timer will be fired in 5 seconds from this very point in time (not when the program finishes). There is nothing put in the event queue just yet

The timer callback is added to the queue immediately. It will be postponed if the event loop ticks before the 5000ms passed. This means the timer will take longer than 5 seconds if the for loop is not finished yet.

then, before 5 seconds is passed I click the document.

This is flawed. If the loop is still running, the UI is blocked and your click doesn't have a chance to get registered before the first alert ("aaa").

5 seconds is past, loop probably is still running and timer we set with setTimeout fires.

Flawed too. We're still on the first tick of the event loop until the for loop is finished. The timer callback can't be fired until then.

like image 141
bfavaretto Avatar answered Jan 29 '26 03:01

bfavaretto



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!