Diego Betto's Blog
Photo by Jan Tinneberg on Unsplash

Diego Betto · 15 settembre 2026 · 5 min di lettura

using e await using: la gestione esplicita delle risorse in JavaScript

Il problema di dimenticare di chiudere una risorsa quando c'è un'eccezione anticipata, come funzionano Symbol.dispose/Symbol.asyncDispose, e un confronto pratico con try/finally.

Condividi:XLinkedInFacebookWhatsApp

Qualsiasi risorsa che ha bisogno di una pulizia esplicita — una connessione al database, un file handle, un lock — condivide la stessa modalità di fallimento: è facile dimenticare la chiamata di pulizia su uno dei percorsi di uscita, specialmente quando un’eccezione può saltarla del tutto. using e await using sono nuove dichiarazioni pensate apposta per rendere questo errore strutturalmente difficile da scrivere.

Il problema: pulizia che viene saltata a causa di un’eccezione

function processFile(path) {
  const file = openFile(path);
  const data = file.read(); // lancia un errore — parseData non gira mai, ma nemmeno file.close()
  const result = parseData(data);
  file.close();
  return result;
}

Se file.read() lancia un errore, l’esecuzione salta fuori direttamente dalla funzione — file.close(), che sta dopo la riga fallita, non gira mai. La risorsa perde. La correzione tradizionale è try/finally:

function processFile(path) {
  const file = openFile(path);
  try {
    const data = file.read();
    return parseData(data);
  } finally {
    file.close();
  }
}

Funziona, ma è opzionale e facile da dimenticare — niente ti obbliga ad avvolgere la risorsa in un try/finally, e con più risorse, l’annidamento diventa presto ingestibile.

using: pulizia automatica, senza bisogno di try/finally

function processFile(path) {
  using file = openFile(path);
  const data = file.read(); // se questo lancia un errore, file viene comunque smaltito automaticamente
  return parseData(data);
}

using dichiara una variabile nello stesso modo di const, ma registra anche il valore per la pulizia automatica alla fine del blocco che la racchiude — sia che quel blocco esca normalmente, tramite return, o tramite un’eccezione lanciata. Nessun blocco finally da ricordare, nessun rischio che una chiamata di pulizia stia dopo una riga che può lanciare un errore.

Come funziona: Symbol.dispose

Perché un valore funzioni con using, ha bisogno di un metodo su Symbol.dispose — è questo che viene effettivamente chiamato all’uscita dal blocco:

function openFile(path) {
  const handle = nativeOpen(path);
  return {
    read: () => nativeRead(handle),
    [Symbol.dispose]() {
      nativeClose(handle);
    },
  };
}

Non sei limitato a usare using su risorse fornite da librerie — qualsiasi oggetto che scrivi con un metodo [Symbol.dispose] si collega allo stesso meccanismo.

await using: l’equivalente asincrono

Alcune pulizie sono esse stesse asincrone — chiudere una connessione al database spesso richiede di fare await su un round-trip di rete. await using gestisce esattamente questo, chiamando [Symbol.asyncDispose] e aspettandolo:

async function withConnection(query) {
  await using conn = await openDatabaseConnection();
  return await conn.execute(query);
  // conn[Symbol.asyncDispose]() viene chiamato e atteso automaticamente qui,
  // sia che execute() sia riuscito, sia uscito prima, sia che abbia lanciato un errore
}

function openDatabaseConnection() {
  return {
    execute: (query) => runQuery(query),
    async [Symbol.asyncDispose]() {
      await closeConnectionGracefully();
    },
  };
}

Più risorse, pulite in ordine inverso

function processFiles(pathA, pathB) {
  using fileA = openFile(pathA);
  using fileB = openFile(pathB);

  return merge(fileA.read(), fileB.read());
  // fileB viene smaltito per primo, poi fileA — ordine inverso alla dichiarazione,
  // la stessa convenzione che l'annidamento manuale di try/finally richiederebbe di scrivere a mano
}

Questo rispecchia esattamente come annideresti a mano i blocchi try/finally per più risorse — using lo fa semplicemente senza l’annidamento.

⚠ Non per valori senza un metodo di dispose

using/await using funzionano solo con valori che implementano Symbol.dispose/ Symbol.asyncDispose. Il normale const resta corretto per i valori ordinari — usa using specificamente quando tieni in mano qualcosa che ha bisogno di uno smaltimento esplicito.

using vs try/finally a colpo d’occhio

try/finally using / await using
Richiede annidamento esplicito dei blocchi No
Facile da dimenticare Sì — niente lo impone Più difficile — la dichiarazione stessa porta la pulizia
Più risorse Blocchi annidati, verboso Dichiarazioni piatte, pulizia automatica in ordine inverso
Funziona con pulizia asincrona Sì, manualmente con await nel finally Sì, nativamente con await using

FAQ

❓ Serve una libreria per usarlo già oggi?

Nessun polyfill necessario per la sintassi sui motori che la supportano — Node.js e i browser moderni stanno progressivamente offrendo il supporto. Verifica la versione del tuo runtime target prima di affidartici senza un transpiler (Babel/TypeScript possono compilare using a un target più vecchio).

❓ Le classi esistenti funzionano con using senza modifiche?

Solo se implementano già Symbol.dispose o Symbol.asyncDispose, oppure le avvolgi in un piccolo oggetto adapter che lo fa. Non è automatico per classi esistenti arbitrarie — tu (o l’autore della libreria) dovete aggiungere il metodo esplicitamente.

❓ È simile al with di Python o allo using di C#?

Concettualmente, sì — è la stessa categoria di feature: una dichiarazione che lega la durata di vita di una risorsa a uno scope lessicale e garantisce la pulizia all’uscita. La versione di JavaScript segue la stessa idea di fondo che quei linguaggi hanno reso popolare.

Conclusione

using e await using eliminano il modo più comune in cui la pulizia delle risorse viene saltata: un’eccezione che salta oltre la riga che avrebbe dovuto chiudere le cose. Implementa Symbol.dispose/Symbol.asyncDispose su qualsiasi cosa abbia bisogno di uno smaltimento esplicito, e il linguaggio si occupa di chiamarlo — nell’ordine giusto, su ogni percorso di uscita — senza un blocco try/finally da scrivere o dimenticare.

Riferimenti

Condividi:XLinkedInFacebookWhatsApp
Diego Betto

Scritto da

Diego Betto

Co-Fondatore & CTO presso PAPION. Senior full-stack engineer specializzato in React, TypeScript, Node.js e sicurezza applicativa.