Comprendere l'Event Loop in JavaScript

Scopri come funziona l'Event Loop in JavaScript e perché è fondamentale per la programmazione asincrona.

Pubblicato:

Introduzione

JavaScript è un linguaggio single-threaded, il che significa che le operazioni vengono eseguite una alla volta durante l'esecuzione di un programma, utilizzando un singolo Call Stack.

Ma cosa succede quando abbiamo operazioni laboriose che richiedono molto tempo per essere eseguite? Immagina una chiamata di rete che impiega 5 secondi per completarsi. Questo bloccherebbe la nostra applicazione, impedendo ad altre operazioni di essere eseguite. Qui entra in gioco l'event loop.

Le operazioni che non provengono direttamente da JavaScript, ma dal browser o da Node.js, sono chiamate Web APIs nel contesto del browser e Node.js APIs nel contesto di Node.js. Esempi includono chiamatefetch, setTimeout e operazioni di lettura e scrittura di file. Queste operazioni vengono delegate alle rispettive API e, una volta completate, le loro callback vengono inserite nella Task Queue, permettendo al Call Stack di rimanere libero per altre funzioni, mantenendo la nostra applicazione reattiva.

Vediamo nel dettaglio i singoli componenti dell'event loop.

Call Stack

Il Call Stack è una struttura dati che gestisce l'ordine di esecuzione delle funzioni in JavaScript. Quando una funzione viene chiamata, viene aggiunta in cima al Call Stack. Una volta completata, viene rimossa. Questo meccanismo permette a JavaScript di eseguire una funzione alla volta, mantenendo l'ordine corretto di esecuzione e garantendo il flusso logico del programma.

Le Web APIs

Le Web APIs sono interfacce fornite dal browser per eseguire operazioni asincrone come chiamate di rete, timer e manipolazione del DOM. Queste operazioni vengono delegate alle Web APIs, che le gestiscono separatamente dal Call Stack di JavaScript. Una volta completate, le callback delle Web APIs vengono inserite nella Task Queue, da dove l'event loop le sposta nel Call Stack per l'esecuzione. Questo consente a JavaScript di rimanere reattivo e di gestire operazioni asincrone senza bloccare l'esecuzione del programma.

Task Queue

La Task Queue, o Callback Queue, è una coda in cui vengono inserite le callback delle operazioni asincrone una volta che le Web APIs le hanno completate. Quando il Call Stack è vuoto, l'event loop sposta la prima callback dalla Task Queue al Call Stack per essere eseguita. Questo meccanismo permette a JavaScript di gestire in modo efficiente operazioni asincrone, assicurando che le funzioni principali non vengano bloccate da operazioni lunghe.

Microtasks

Oltre alla Task Queue, esiste anche la Microtask Queue, che ha una priorità più alta. Le microtasks includono Promise e MutationObserver. Le microtasks vengono eseguite subito dopo l'operazione corrente e prima che l'event loop continui con la Task Queue.

Event Loop

L'event loop è un ciclo continuo che controlla se il Call Stack è vuoto. Se il Call Stack è vuoto e ci sono callback nella Task Queue, l'event loop sposta la prima callback dalla Task Queue al Call Stack, permettendone l'esecuzione.

Esempio

console.log('Inizio');

setTimeout(() => {
  console.log('Timeout scaduto');
}, 0);

Promise.resolve().then(() => {
  console.log('Promise risolta');
});

console.log('Fine');

Spiegazione

1. Sincrono: console.log('Inizio') econsole.log('Fine') vengono eseguiti immediatamente.

2. Microtask: La promessa (Promise.resolve().then()) viene gestita subito dopo il codice sincrono.

3. Macrotask: setTimeout() è gestito dopo che tutte le microtask sono completate.

Output:


Inizio
Fine
Promise risolta
Timeout scaduto
        

In breve, l'event loop esegue prima il codice sincrono, poi le Promise (microtask) e infine i timer (macrotask).