Please note, this is a STATIC archive of website developer.mozilla.org from 03 Nov 2016, cach3.com does not collect or store any user information, there is no "phishing" involved.

Revision 1075548 of Concurrency model and Event Loop

  • Revision slug: Web/JavaScript/EventLoop
  • Revision title: Concurrency model and Event Loop
  • Revision id: 1075548
  • Created:
  • Creator: fscholz
  • Is current revision? No
  • Comment clr review; thx!

Revision Content

{{JsSidebar("Advanced")}}

JavaScript has a concurrency model based on an "event loop". This model is quite different than the model in other languages like C or Java.

Runtime concepts

The following sections explain a theoretical model. Modern JavaScript engines implement and optimize heavily the described semantics.

Visual representation

Stack, heap, queue

Stack

Function calls form a stack of frames.

function f(b){
  var a = 12;
  return a+b+35;
}

function g(x){
  var m = 4;
  return f(m*x);
}

g(21);

When calling g, a first frame is created containing g arguments and local variables. When g calls f, a second frame is created and pushed on top of the first one containing f arguments and local variables. When f returns, the top frame element is popped out of the stack (leaving only g call frame). When g returns, the stack is empty.

Heap

Objects are allocated in a heap which is just a name to denote a large mostly unstructured region of memory.

Queue

A JavaScript runtime contains a message queue, which is a list of messages to be processed. A function is associated to each message. When the stack is empty, a message is taken out of the queue and processed. The processing consists of calling the associated function (and thus creating an initial stack frame). The message processing ends when the stack becomes empty again.

Event loop

The event loop got its name because of how it's usually implemented, which usually resembles:

while(queue.waitForMessage()){
  queue.processNextMessage();
}

queue.waitForMessage waits synchronously for a message to arrive if there is none currently.

"Run-to-completion"

Each message is processed completely before any other message is processed. This offers some nice properties when reasoning about your program, including the fact that whenever a function runs, it cannot be pre-empted and will run entirely before any other code runs (and can modify data the function manipulates). This differs from C, for instance, where if a function runs in a thread, it can be stopped at any point to run some other code in another thread.

A downside of this model is that if a message takes too long to complete, the web application is unable to process user interactions like click or scroll. The browser mitigates this with the "a script is taking too long to run" dialog. A good practice to follow is to make message processing short and if possible cut down one message into several messages.

Adding messages

In web browsers, messages are added any time an event occurs and there is an event listener attached to it. If there is no listener, the event is lost. So a click on an element with a click event handler will add a message--likewise with any other event.

Calling setTimeout will add a message to the queue after the time passed as a second argument. If there is no other message in the queue, the message is processed right away; however, if there are messages, the setTimeout message will have to wait for other messages to be processed. For that reason the second argument indicates a minimum time and not a guaranteed time.

Zero delays

Zero delay doesn't actually mean the call back will fire-off after zero milliseconds. Calling {{domxref("WindowTimers.setTimeout", "setTimeout")}} with a delay of 0 (zero) milliseconds doesn't execute the callback function after the given interval. The execution depends on the number of awaiting tasks in the queue. In the example below the message ''this is just a message'' will be written to the console before the message in the callback gets processed, because the delay is the minimum time required for the runtime to process the request, but not a guaranteed time.

(function () {

  console.log('this is the start');

  setTimeout(function cb() {
    console.log('this is a msg from call back');
  });

  console.log('this is just a message');

  setTimeout(function cb1() {
    console.log('this is a msg from call back1');
  }, 0);

  console.log('this is the  end');

})();

Several Runtime communicating together

A web worker or a cross-origin iframe has its own stack, heap, and message queue. Two distinct runtimes can only communicate through sending messages via the postMessage method. This method adds a message to the other runtime if the latter listens to message events.

Never blocking

A very interesting property of the event loop model is that JavaScript, unlike a lot of other languages, never blocks. Handling I/O is typically performed via events and callbacks, so when the application is waiting for an IndexedDB query to return or an XHR request to return, it can still process other things like user input.

Legacy exceptions exist like alert or synchronous XHR, but it is considered as a good practice to avoid them. Beware, exceptions to the exception do exist (but are usually implementation bugs rather than anything else).

Revision Source

<div>{{JsSidebar("Advanced")}}</div>

<p>JavaScript has a concurrency model based on an "event loop". This model is quite different than the model in other languages like C or Java.</p>

<h2 id="Runtime_concepts">Runtime concepts</h2>

<p>The following sections explain a theoretical model. Modern JavaScript engines implement and optimize heavily the described semantics.</p>

<h3 id="Visual_representation">Visual representation</h3>

<p style="text-align: center;"><img alt="Stack, heap, queue" src="/files/4617/default.svg" style="height:270px; width:294px" /></p>

<h3 id="Stack">Stack</h3>

<p>Function calls form a stack of <em>frames</em>.</p>

<pre class="brush: js">
function f(b){
  var a = 12;
  return a+b+35;
}

function g(x){
  var m = 4;
  return f(m*x);
}

g(21);
</pre>

<p>When calling <code>g</code>, a first frame is created containing <code>g</code> arguments and local variables. When <code>g</code> calls <code>f</code>, a second frame is created and pushed on top of the first one containing <code>f</code> arguments and local variables. When <code>f</code> returns, the top frame element is popped out of the stack (leaving only <code>g</code> call frame). When <code>g</code> returns, the stack is empty.</p>

<h3 id="Heap">Heap</h3>

<p>Objects are allocated in a heap which is just a name to denote a large mostly unstructured region of memory.</p>

<h3 id="Queue">Queue</h3>

<p>A JavaScript runtime contains a message queue, which is a list of messages to be processed. A function is associated to each message. When the stack is empty, a message is taken out of the queue and processed. The processing consists of calling the associated function (and thus creating an initial stack frame). The message processing ends when the stack becomes empty again.</p>

<h2 id="Event_loop">Event loop</h2>

<p>The <code>event loop</code> got its name because of how it's usually implemented, which usually resembles:</p>

<pre class="brush: js">
while(queue.waitForMessage()){
  queue.processNextMessage();
}</pre>

<p><code>queue.waitForMessage</code> waits synchronously for a message to arrive if there is none currently.</p>

<h3 id="Run-to-completion">"Run-to-completion"</h3>

<p>Each message is processed completely before any other message is processed. This offers some nice properties when reasoning about your program, including the fact that whenever a function runs, it cannot be pre-empted and will run entirely before any other code runs (and can modify data the function manipulates). This differs from C, for instance, where if a function runs in a thread, it can be stopped at any point to run some other code in another thread.</p>

<p>A downside of this model is that if a message takes too long to complete, the web application is unable to process user interactions like click or scroll. The browser mitigates this with the "a script is taking too long to run" dialog. A good practice to follow is to make message processing short and if possible cut down one message into several messages.</p>

<h3 id="Adding_messages">Adding messages</h3>

<p>In web browsers, messages are added any time an event occurs and there is an event listener attached to it. If there is no listener, the event is lost. So a click on an element with a click event handler will add a message--likewise with any other event.</p>

<p>Calling <code><a href="/en-US/docs/Web/API/WindowTimers.setTimeout">setTimeout</a></code> will add a message to the queue after the time passed as a second argument. If there is no other message in the queue, the message is processed right away; however, if there are messages, the <code>setTimeout</code> message will have to wait for other messages to be processed. For that reason the second argument indicates a minimum time and not a guaranteed time.</p>

<h3 id="Zero_delays">Zero delays</h3>

<p>Zero delay doesn't actually mean the call back will fire-off after zero milliseconds. Calling {{domxref("WindowTimers.setTimeout", "setTimeout")}} with a delay of 0 (zero) milliseconds doesn't execute the callback function after the given interval. The execution depends on the number of awaiting&nbsp;tasks in the queue. In the example below the message ''this is just a message'' will be written to the console before the message in the callback gets processed, because the delay is the minimum time required for the runtime to process the request, but not a guaranteed time.</p>

<pre class="brush: js">
(function () {

  console.log('this is the start');

  setTimeout(function cb() {
    console.log('this is a msg from call back');
  });

  console.log('this is just a message');

  setTimeout(function cb1() {
    console.log('this is a msg from call back1');
  }, 0);

  console.log('this is the  end');

})();</pre>

<h3 id="Several_Runtime_communicating_together">Several Runtime communicating together</h3>

<p>A web worker or a cross-origin <code>iframe</code> has its own stack, heap, and message queue. Two distinct runtimes can only communicate through sending messages via the <a href="/en-US/docs/DOM/window.postMessage"><code>postMessage</code></a> method. This method adds a message to the other runtime if the latter listens to <code>message</code> events.</p>

<h2 id="Never_blocking">Never blocking</h2>

<p>A very interesting property of the event loop model is that JavaScript, unlike a lot of other languages, never blocks. Handling I/O is typically performed via events and callbacks, so when the application is waiting for an <a href="/en-US/docs/Web/API/IndexedDB_API">IndexedDB</a> query to return or an <a href="/en-US/docs/Web/API/XMLHttpRequest">XHR</a> request to return, it can still process other things like user input.</p>

<p>Legacy exceptions exist like <code>alert</code> or synchronous XHR, but it is considered as a good practice to avoid them. Beware, <a href="https://stackoverflow.com/questions/2734025/is-javascript-guaranteed-to-be-single-threaded/2734311#2734311">exceptions to the exception do exist</a> (but are usually implementation bugs rather than anything else).</p>
Revert to this revision