# The JS Event Loop
# Introducción al Event Loop
All JavaScript environments use an event loop
As long as there’s something left to do, JSs event loop will keep spinning. Whenever an event occurs, JS invokes any callbacks (event handlers) that are listening for that event.
The general algorithm of the JS Engine is like this:
executeSequentially(mainTask);
while (thereArePendingTasks()) do
let oldestFinishedTask = waitUntilSomeTaskIsReady();
executeSequentially(oldestFinishedTask);
end do
2
3
4
5
There’s an endless loop, when JavaScript engine waits for tasks, executes them and then sleeps waiting for more tasks
When JS runs in the browser:
Rendering never happens while the engine executes a task. Doesn’t matter if the task takes a long time. Changes to DOM are painted only after the task is complete.
If a task takes too long, the browser can’t do other tasks, process user events, so after a time it raises an alert like Page Unresponsive suggesting to kill the task with the whole page. That happens when there are a lot of complex calculations or a programming error leading to infinite loop.
There is in fact a Render queue, which is another queue in the event loop system which basically takes care of all tasks to be done before every screen update or repaints.
Your JavaScript code runs single threaded. There is just one thing happening at a time.
- Pay attention to how you write your code and avoid anything that could block the thread, like synchronous network calls or long loops.
- In most browsers there is an event loop for every browser tab, to avoid a web page with heavy processing to block your entire browser.
- Web Workers run in their own event loop as well
Quote from MDN (opens new window):
Each message is processed completely before any other message is processed [...] 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.
MDN utiliza la terminología cola de mensajes para la cola de callbacks:
A JavaScript runtime uses a message queue, which is a list of messages to be processed. Each message has an associated function which gets called in order to handle the message.
# Unas Preguntas
# La pila de LLamadas
console.trace
muestra la stack trace (opens new window) en la consola. la stack trace es el camino de las llamadas hasta el punto: { nombre-de-rutina, fichero-del-programa, nº de línea, nº de columna}
¿Cual es la salida de las llamadas a console.trace
?
1 function multiply(x,y) {
2 // console.trace imprime una traza de la pila
3 console.trace("-----------At multiply-----------");
4 return x * y;
5 }
6
7 function squared(n) {
8 console.trace("-----------At squared-----------");
9 return multiply(n,n)
10 }
11
12 function printSquare(n) {
13 return squared(n)
14 }
15
16 let numberSquared = printSquare(5);
17 console.log(numberSquared);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Orden de Ejecución
¿En que orden ocurren las salidas?
(function() {
console.log('this is the start');
setTimeout(function cb() {
console.log('Callback 1: this is a msg from call back');
}); // has a default time value of 0
console.log('this is just a message');
setTimeout(function cb1() {
console.log('Callback 2: this is a msg from call back');
}, 0);
console.log('this is the end');
})();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- El método
setTimeout(funcion, retraso)
establece un temporizador que ejecutafuncion
después de que transcurre un tiemporetraso
en milisegundos. - Si se omite este parámetro se usa el valor 0.
- El valor retornado identifica el temporizador creado con la llamada a
setTimeout()
; este valor puede pasarse aclearTimeout()
para cancelar el temporizador.
# Hoist: Cual es la salida?
console.log(hoist);
var hoist = 'The variable has been hoisted.';
2
3
¿Cual sería la salida de esta variante?
console.log(hoist);
hoist = 'The variable has been hoisted.'
2
3
Para responder a esta pregunta es conveniente tener claro el concepto de Hoisting (opens new window) en JS. Hoisting is a JavaScript mechanism where variables, function and class declarations are moved to the top of their scope before code execution. Hoisting only moves the declaration. The assignments are left in place.
The scope of a variable declared with the keyword var
is its current execution context. This is either the enclosing function
or for variables declared outside any function, global
.
Véase Understanding Hoisting in JavaScript by Mabishi Wakio (opens new window)
# Hoisting y Asíncronía ¿Cual es la salida?
for(var i=0;i<=3; i++) {
setTimeout(()=> console.log(i), 0)
}
2
3
➜ event-loop-examples-1920 git:(master) ✗ pwd -P
/Users/casianorodriguezleon/campus-virtual/1920/sytws1920/apuntes/tema1-introduccion/practicas/p2-t1-c3-file-system/event-loop
➜ event-loop-examples-1920 git:(master) ✗ ls
README.md hoist.js splitting-cpu-hungry-task.html
animated-circle-with-promise.html how-the-event-loop-works.png var.js
callstack.js order.js
event-loop.png settimeout-does-not-run-inmediately.js
2
3
4
5
6
7
# JS is single threaded
¿Cual es la salida?
const s = new Date().getSeconds();
setTimeout(function() {
console.log("Ran after " + (new Date().getSeconds() - s) + " seconds");
}, 500);
while(true) {
if(new Date().getSeconds() - s >= 2) {
console.log("Good, looped for 2 seconds");
break;
}
}
2
3
4
5
6
7
8
9
10
11
12
- Si no proporciona argumentos, el constructor
new Date()
crea un objetoDate
con la hora y fecha de hoy según la hora local. - El método
getSeconds()
devuelve un número entre 0 y 59 representando los segundos en la fecha especificada de acuerdo a la hora local.
# Repasando las Preguntas a la luz del Bucle de Eventos
# Ejemplo: La Pila de Llamadas
Este ejemplo es tomado del vídeo:
se le puede proporcionar a loupe
:
- loupe (opens new window) a tool in the cloud to see the event loop working
Está en este directorio en mi laptop:
~/campus-virtual/1920/sytws1920/ull-mii-sytws-1920.github.io/tema1-introduccion/practicas/p2-t1-c3-file-system/event-loop/callstack.js
Este es el código:
function multiply(x,y) {
// console.trace imprime una traza de la pila
console.trace("-----------At multiply-----------");
return x * y;
}
function squared(n) {
console.trace("-----------At squared-----------");
return multiply(n,n)
}
function printSquare(n) {
return squared(n)
}
let numberSquared = printSquare(5);
console.log(numberSquared);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Orden de Ejecución
Directorio en mi máquina:
tema1-introduccion/practicas/p2-t1-c3-file-system/event-loop/order.js
Ejemplo sacado del Tutorial Concurrency model and Event Loop (opens new window) at https://developer.mozilla.org (opens new window)
(function() {
console.log('this is the start');
setTimeout(function cb() {
console.log('Callback 1: this is a msg from call back');
}); // has a default time value of 0
console.log('this is just a message');
setTimeout(function cb1() {
console.log('Callback 2: this is a msg from call back');
}, 0);
console.log('this is the end');
})();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Ejemplo: JS is single threaded
En mi máquina:
tema1-introduccion/practicas/p2-t1-c3-file-system/event-loop/settimeout-does-not-run-inmediately.js
Tomado del tutorial:
- Tutorial Concurrency model and Event Loop (opens new window) at https://developer.mozilla.org (opens new window)
Primero, un ejemplo, para entender el funcionamiento de Date
y getSeconds
:
[~/.../p2-t1-c3-file-system/event-loop(master)]$ node
Welcome to Node.js v12.10.0.
Type ".help" for more information.
> d = new Date()
2020-02-16T10:07:51.682Z
> s = d.getSeconds()
51
> e = new Date()
2020-02-16T10:07:57.992Z
> e.getSeconds()-d.getSeconds()
6
2
3
4
5
6
7
8
9
10
11
¿Cual es la salida?
const s = new Date().getSeconds();
setTimeout(function() {
console.log("Ran after " + (new Date().getSeconds() - s) + " seconds");
}, 500);
while(true) {
if(new Date().getSeconds() - s >= 2) {
console.log("Good, looped for 2 seconds");
break;
}
}
2
3
4
5
6
7
8
9
10
11
12
# Race Condition
See the examples:
- A Simple Example: Loading an Image with Some Delay
- Yes, there are race conditions in JavaScript (opens new window) by Yuval Greenfield (opens new window): An example that manually interleaves promises to produce a somehow artificial race condition
# The Event Loop en el libro The Modern JavaScript Tutorial (opens new window)
- Event Loop (opens new window)
- Use-case 1: splitting CPU-hungry tasks (opens new window)
- Use case 2: progress indication (opens new window)
- Use case 3: doing something after the event (opens new window)
- Macrotasks and Microtasks (opens new window)
- Summary (opens new window)
# The section Concurrency model and the event loop at https://developer.mozilla.org/
- Runtime concepts (opens new window)
- Event loop (opens new window)
- Never blocking (opens new window)
- Specifications (opens new window)
# References
- Event loop: microtasks and macrotasks (opens new window) en el libro https://javascript.info
- Tutorial Concurrency model and Event Loop (opens new window) at https://developer.mozilla.org
- The JavaScript Event Loop (opens new window) by Flavio
- Concurrency and Parallel Computing in JavaScript (opens new window) Stephan Herhut. Strange Loop 2013.
- Philip Roberts: ¿Que diablos es el "event loop" (bucle de eventos) de todos modos? (JSConf EU) (opens new window)
- loupe (opens new window) a tool in the cloud to see the event loop working
- Chinenye Onuegbu: Code examples for JSDay Canarias 2019 (opens new window)