Event Loop
The JavaScript interpreter is single-threaded. It handles call-backs, timers, and other asynchronous operations using an event loop. Once a page is loaded, it enters the event loop. The loop uses a FIFO-organized task list, and list of active timers, and the binding of events. The pseudocode below represents the behavior of the event loop.
// tasks is a list of code blocks to run. tasks ← [ contents of each <script> tag ] // timers is a list of pairs, <time, task> for timeouts, or triples // <time, task, increment> for intervals timers ← [ ] // bindings is a map from event types to tasks bindings ← [ bindings specified in HTML on−x attributes ] while true do if tasks is empty then sleep until: currenttime() ≥ earliest(timers) or some event e occurs, where bindings[e] exists while tasks is not empty then t ← pop front of task list run t endif while timers is not empty and earliest(timers).time ≤ currenttime() do x ← pop earliest(timers) from timers push x.task onto the back of tasks if x.increment exists // an interval push <x.time+x.increment, x.task, x.increment> onto timers endif done for each pending event e where bindings[e] exists do push bindings[e] onto tasks done done

An “event” includes the usual things: mouse clicks, keyboard events, network events, browser events.

Note that when a task t is run, the event loop is just waiting for it to return; in particular, it is not running other tasks. That means that no event handler can be interrupted while it is running. There is no need for locks or synchronization as in a threaded app. The execution of t may add tasks to the queue, or bound events may occur while t is running, but they won't be handled until after t completes. So, if you try to see how fast you can click a button, the handler calls will never overlap. You'll just make the queue longer. This allows JavaScript to provide a form of asynchronous programming which is not general threading, but has the advantage of not needing explicit locks and waits.

It also means that, while t is running, any other event (asynchronous or timed) is delayed until afterwards. You need to be sure none of your handlers takes a long time, since that will freeze the browser window. This is one of the reasons not to use alert, since it won't return until you dismiss the window.

You can also see how timed events may be delayed. Any setTimeout or setInterval execution adds a timer to the list, and if nothing else is going on, the wait will end just in time to run it. But if things are busy, or if several events are secheduled at once, the loop may be running some other t, and won't get around to the timed event until it's a bit late.

As noted, the event list must have FIFO behavior for the interpreter to operate correctly. Some sort of linked list would seem the obvious choice. The timer list is essentially a priority queue, since you add timers in the order the code requires, and remove them in ascending time order, so a heap would seem to be the obvious choice. But the correctness of the system depends on its behavior, not its data structures.