JavaScript

Una dashboard che si aggiorna da sola: polling intelligente in JavaScript

calendar_today personTeam EGSOFT schedule4 min di lettura
info

Disclaimer: le informazioni pubblicate in questa sezione hanno finalità di pura divulgazione tecnica. EG Software S.r.l. non si assume alcuna responsabilità per un utilizzo improprio dei contenuti, né per eventuali danni diretti o indiretti derivanti dalla loro applicazione, e non garantisce l'aggiornamento, l'accuratezza o la completezza delle informazioni riportate. Prima di utilizzare in produzione codice o procedure qui descritte, verificane sempre l'adeguatezza al proprio contesto.

Molte dashboard interne — il pannello di controllo di un dispositivo, lo stato di una coda di lavoro, i sensori di un piccolo impianto — non hanno bisogno di un WebSocket: basta interrogare periodicamente il server con fetch e aggiornare la pagina. Il punto delicato non è la chiamata in sé, ma farla bene: mostrare un indicatore di caricamento senza sfarfallio, non continuare a interrogare il server quando la scheda del browser non è nemmeno visibile, e confermare le azioni distruttive senza usare il vecchio confirm() del browser.

Un helper fetch con indicatore di caricamento condiviso

Se più richieste possono essere in volo insieme (per esempio più widget che si aggiornano in parallelo), un semplice booleano "sto caricando" non basta: bisogna contare le richieste attive e nascondere l'indicatore solo quando l'ultima è finita.

let richiesteAttive = 0;

function mostraCaricamento() {
  richiesteAttive++;
  document.getElementById('barraCaricamento').classList.add('attiva');
}

function nascondiCaricamento() {
  richiesteAttive = Math.max(0, richiesteAttive - 1);
  if (richiesteAttive === 0) {
    document.getElementById('barraCaricamento').classList.remove('attiva');
  }
}

async function apiGet(percorso, { silenzioso = false } = {}) {
  if (!silenzioso) mostraCaricamento();
  try {
    const risposta = await fetch(percorso);
    const dati = await risposta.json();
    if (!risposta.ok || dati.success === false) {
      throw new Error(dati.error || `Errore HTTP ${risposta.status}`);
    }
    return dati;
  } finally {
    if (!silenzioso) nascondiCaricamento();
  }
}

L'opzione silenzioso è pensata proprio per il polling automatico in background: un aggiornamento che l'utente non ha richiesto esplicitamente non deve dare l'impressione che la pagina si sia bloccata.

Fermare il polling quando la scheda non è visibile

Un setInterval ingenuo continua a interrogare il server anche se la scheda è in un'altra finestra da ore. La Page Visibility API permette di saltare questi cicli inutili e di forzare un aggiornamento immediato non appena la scheda torna visibile:

async function aggiornaStato() {
  if (document.hidden) return; // nessuno la sta guardando, si salta il giro

  try {
    const dati = await apiGet('/api/status', { silenzioso: true });
    document.getElementById('badgeStato').textContent =
      `attivo · uptime ${dati.uptime_secondi}s`;
  } catch (errore) {
    document.getElementById('badgeStato').textContent = `errore: ${errore.message}`;
  }
}

aggiornaStato();
setInterval(aggiornaStato, 10000);
document.addEventListener('visibilitychange', () => {
  if (!document.hidden) aggiornaStato();
});

Confermare un'azione distruttiva con <dialog>

Per eliminazioni o azioni non reversibili, un <dialog> nativo si comporta meglio del vecchio confirm(): può essere stilizzato, gestisce da solo il tasto Escape e restituisce facilmente una Promise da attendere con await:

function confermaAzione(messaggio) {
  const dialog = document.getElementById('dialogConferma');
  dialog.querySelector('.messaggio').textContent = messaggio;

  return new Promise((resolve) => {
    const bottoneOk = dialog.querySelector('.btn-conferma');
    const bottoneAnnulla = dialog.querySelector('.btn-annulla');

    const chiudi = (risultato) => {
      bottoneOk.removeEventListener('click', suOk);
      bottoneAnnulla.removeEventListener('click', suAnnulla);
      dialog.close();
      resolve(risultato);
    };
    const suOk = () => chiudi(true);
    const suAnnulla = () => chiudi(false);

    bottoneOk.addEventListener('click', suOk);
    bottoneAnnulla.addEventListener('click', suAnnulla);
    dialog.addEventListener('cancel', suAnnulla, { once: true }); // tasto Escape
    dialog.showModal();
  });
}

// Utilizzo:
bottoneElimina.addEventListener('click', async () => {
  if (await confermaAzione('Eliminare questo elemento?')) {
    await apiDelete(`/api/elementi/${id}`);
  }
});

Quando conviene ancora il polling

Un WebSocket o un canale SSE restano la scelta migliore quando gli aggiornamenti sono frequenti (più volte al secondo) o quando serve una notifica immediata. Per stati che cambiano ogni pochi secondi, però, un polling ben scritto — silenzioso, sospeso quando la pagina non è visibile, con un indicatore di caricamento onesto — è più semplice da mantenere e non richiede un server con gestione delle connessioni persistenti.

Altri articoli su JavaScript grid_viewTutte le tematiche
Scrivici su WhatsApp