⚡ Prestazioni web

INP: ridurre la latenza delle interazioni sul main thread

Cosa misura l'Interaction to Next Paint, perché clic e tocchi sembrano lenti e come spezzare i task lunghi di JavaScript per ridare il controllo all'utente.

L'Interaction to Next Paint ha sostituito il First Input Delay tra i Core Web Vitals nel marzo 2024. La differenza è sostanziale: il vecchio indicatore guardava solo il ritardo della prima interazione, mentre l'INP osserva tutte le interazioni della visita (clic, tocchi, pressioni di tasti) e riporta un valore vicino alla peggiore. Un INP fino a 200 millisecondi è considerato buono, oltre 500 è scarso. Chi ha un sito con molte interazioni, come un configuratore o un'area riservata, se ne accorge subito.

Le tre parti di un'interazione

Ogni interazione misurata dall'INP si compone di tre tempi:

  1. ritardo di input: il tempo tra il gesto dell'utente e l'avvio dei gestori di evento. È lungo quando il main thread è già occupato da un altro task;
  2. tempo di elaborazione: quanto impiegano i gestori click, keydown, pointerup e simili a terminare;
  3. ritardo di presentazione: il tempo che serve al browser per ricalcolare stili, layout e disegnare il fotogramma successivo.

L'utente percepisce la somma: tocca un pulsante e per un istante non succede nulla. Ognuna delle tre parti ha cause e rimedi diversi.

Il nemico principale: i task lunghi

Il browser esegue JavaScript, calcolo degli stili, layout e disegno su un unico thread principale. Un task che dura più di 50 millisecondi è considerato "lungo" perché, mentre è in corso, nessun'altra cosa può accadere. Se l'utente tocca lo schermo durante un task da 300 millisecondi, quel tempo diventa ritardo di input.

Le fonti tipiche di task lunghi sono l'avvio di framework pesanti, script di terze parti che si eseguono tutti insieme, la manipolazione di elenchi con migliaia di elementi e gestori di evento che fanno troppo lavoro in una sola volta.

Restituire il controllo al browser

La tecnica centrale si chiama yielding: spezzare il lavoro in blocchi e, tra un blocco e l'altro, lasciare che il browser gestisca input e disegno. Un approccio diffuso e compatibile usa una promessa con setTimeout:

function cediIlPasso() {
  if ('scheduler' in window && 'yield' in scheduler) {
    return scheduler.yield();
  }
  return new Promise(r => setTimeout(r, 0));
}

async function elaboraRighe(righe) {
  for (let i = 0; i < righe.length; i++) {
    aggiornaRiga(righe[i]);
    if (i % 50 === 0) await cediIlPasso();
  }
}

scheduler.yield() è disponibile nei browser basati su Chromium e ha il vantaggio di riprendere il lavoro con priorità più alta rispetto a un semplice timeout; il fallback garantisce il funzionamento ovunque.

Prima il feedback, poi il resto

Un principio pratico: nel gestore di evento fai solo ciò che serve per mostrare all'utente che l'azione è stata ricevuta, poi rimanda il resto. Se un clic su «Aggiungi al carrello» deve aggiornare il contatore, inviare un evento di analytics, salvare nel localStorage e ricalcolare i suggerimenti, solo il primo punto è urgente.

pulsante.addEventListener('click', async () => {
  contatore.textContent = ++totale;   // visibile subito
  await cediIlPasso();
  inviaAnalytics('aggiunta');
  salvaCarrello();
  ricalcolaSuggerimenti();
});

Il fotogramma con il contatore aggiornato viene disegnato prima che parta il lavoro secondario, e l'INP misura proprio quel fotogramma.

Ridurre il ritardo di presentazione

Anche con gestori veloci, l'interazione può risultare lenta se il browser deve ricalcolare un layout enorme. Le cause più comuni:

  • un DOM con decine di migliaia di nodi, dove anche un piccolo cambiamento di classe sul body costringe a rivalutare molti selettori;
  • letture e scritture di layout alternate (leggere offsetHeight, cambiare uno stile, rileggere), che forzano più ricalcoli nello stesso fotogramma;
  • inserimento di grandi blocchi di HTML con innerHTML in risposta al clic.

Mantenere il DOM contenuto, raggruppare le letture prima delle scritture e aggiornare solo la porzione necessaria sono interventi che si vedono subito.

Come trovare le interazioni lente

In laboratorio, il pannello Performance di DevTools mostra la traccia delle interazioni con la durata delle tre parti; conviene attivare il rallentamento della CPU per simulare un telefono di fascia media, perché su un portatile potente quasi tutto sembra veloce. Sul campo, la libreria web-vitals con attribuzione indica l'elemento su cui è avvenuta l'interazione peggiore e il tipo di evento: raccogliendo questi dati si scopre, per esempio, che l'INP cattivo dipende tutto dal menu a tendina dei filtri di una sola pagina.

Errori comuni

  • Ottimizzare solo il caricamento iniziale e dimenticare che l'INP si misura per tutta la visita.
  • Spostare il lavoro in un setTimeout lungo, che rimanda il problema senza risolverlo se poi il task resta monolitico.
  • Testare solo su desktop: la differenza di potenza di calcolo tra dispositivi rende l'INP una metrica soprattutto mobile.

In sintesi

  • L'INP misura la latenza di tutte le interazioni e riporta un valore vicino alla peggiore; buono fino a 200 ms.
  • È composto da ritardo di input, tempo di elaborazione e ritardo di presentazione.
  • I task lunghi sul main thread sono la causa principale: spezzali e cedi il passo al browser.
  • Mostra subito il feedback visivo e rimanda il lavoro secondario.
  • Misura su dispositivi lenti o con CPU rallentata e raccogli dati reali con attribuzione.
MM
Marco Marzaduri

Marco Marzaduri si occupa di cybersecurity, informatica forense e sviluppo web. Laureato in informatica, unisce il rigore dell'analisi tecnica all'attenzione per le persone che usano la tecnologia.

Leggi anche