Vai al contenuto principale

[Medium] 📄 Event Loop

1. Perché JavaScript ha bisogno dell'asincronia? Spiega callback ed event loop

Perché JavaScript ha bisogno del comportamento asincrono? Spiega callback ed event loop.

JavaScript è fondamentalmente single-threaded. Uno dei suoi compiti principali è manipolare il DOM del browser. Se più thread modificassero lo stesso nodo contemporaneamente, il comportamento diventerebbe molto complesso. Per evitare ciò, JavaScript opera con un modello a thread singolo.

Il comportamento asincrono è la soluzione pratica sopra questo modello. Se un'operazione deve attendere 2 secondi, il browser non può semplicemente bloccarsi. Esegue prima i task sincroni, mentre i task asincroni vengono accodati in una coda dei task.

Il contesto di esecuzione sincrono può essere inteso come il call stack. Il browser esegue prima i task nel call stack. Quando il call stack diventa vuoto, preleva i task in attesa dalla coda dei task e li inserisce nel call stack.

  1. Il browser verifica se il call stack è vuoto => No => continua a eseguire i task del call stack.
  2. Il browser verifica se il call stack è vuoto => Sì => controlla la coda dei task => se ci sono task, ne sposta uno nel call stack.

Questo ciclo ripetuto è il concetto di event loop.

console.log(1);

// Questa funzione asincrona è il callback
setTimeout(function () {
console.log(2);
}, 0);

console.log(3);

// Ordine di output: 1 3 2

2. Perché setInterval non è preciso in termini di tempistica?

Perché setInterval non è preciso nella tempistica?

  1. JavaScript è single-threaded (un task alla volta, gli altri attendono in coda). Se un callback di setInterval impiega più tempo dell'intervallo stesso, il callback successivo viene ritardato. Esempio: l'intervallo è 1 secondo, ma un callback impiega 2 secondi. L'esecuzione successiva è già in ritardo di 1 secondo. Questo sfasamento si accumula nel tempo.

  2. Anche i browser e i runtime impongono limiti. Nella maggior parte dei principali browser (Chrome, Firefox, Safari), l'intervallo minimo pratico è solitamente intorno ai 4ms. Quindi anche se imposti 1ms, spesso viene eseguito circa ogni 4ms.

  3. Un uso intensivo di CPU o memoria può ritardare l'esecuzione. Task come editing video o elaborazione immagini spesso causano ritardi nei timer.

  4. JavaScript ha la garbage collection. Se il callback dell'intervallo crea molti oggetti, il lavoro del GC può introdurre ritardi aggiuntivi.

Alternativa

requestAnimationFrame

Se stai usando setInterval per le animazioni, considera di sostituirlo con requestAnimationFrame.

  • Sincronizzato con il repaint: viene eseguito quando il browser è pronto a disegnare il frame successivo. Questo è molto più preciso che indovinare la tempistica del repaint con setInterval o setTimeout.
  • Migliori prestazioni: poiché si allinea con i cicli di repaint, non viene eseguito quando il repaint non è necessario (ad esempio, nelle tab in background), risparmiando calcoli.
  • Throttling automatico: regola la frequenza di esecuzione in base alle condizioni del dispositivo, tipicamente intorno ai 60 FPS.
  • Timestamp ad alta precisione: riceve un parametro DOMHighResTimeStamp (precisione al microsecondo), utile per tempistiche precise delle animazioni.
Esempio
let startPos = 0;

function moveElement(timestamp) {
// aggiorna la posizione nel DOM
startPos += 5;
document.getElementById(
'myElement'
).style.transform = `translateX(${startPos}px)`;

// Continua l'animazione fino a quando l'elemento raggiunge la posizione target
if (startPos < 500) {
requestAnimationFrame(moveElement);
}
}

// avvia l'animazione
requestAnimationFrame(moveElement);

moveElement() aggiorna la posizione ad ogni frame (tipicamente 60 FPS) fino a raggiungere 500 pixel. Questo di solito produce un'animazione più fluida e naturale rispetto a setInterval.