Future/Promise — cos’è, computazioni asincrone e programmazione

Autore: IT Sectr Pubblicato: 2026-03-17 Tempo di lettura: 9 min

Future/Promise è un pattern di programmazione asincrona che rappresenta un contenitore per il risultato di un’operazione non ancora completata. Secondo Dart Documentation, 2025, Future è un risultato in sola lettura di computazioni differite, Promise è un contratto in sola scrittura che riempie questo risultato. Future viene utilizzato in Dart, Java, Scala e C++ per catene then/catch.

Punti chiave

  • Future — un contenitore in sola lettura per un risultato asincrono disponibile in futuro
  • Promise — un segnaposto in sola scrittura che accetta il risultato e lo passa al Future
  • Catene then — elaborazione sequenziale di operazioni asincrone senza callback annidati
  • Future in Dart — la base di async/await e delle librerie dio, http, shelf
  • Future/Promise — un’alternativa al pattern callback e la base per la sintassi async/await

Cos’è Future/Promise?

Future/Promise è una coppia di oggetti interconnessi che dividono un risultato asincrono in due ruoli. Promise può scrivere il risultato (resolve) o un errore (reject). Future può solo leggerlo — attraverso callback (then, catchError) o tramite await. Questa separazione garantisce che il consumatore non possa modificare il risultato, ma solo utilizzarlo.

Future in Dart

Il linguaggio Dart utilizza Future come suo principale meccanismo asincrono. Future<T> è un contratto per ricevere un valore di tipo T in futuro. Un Future può essere in uno dei tre stati: unresolved (operazione in corso), resolved with value (completato con successo) o resolved with error (completato con errore). Una volta passato allo stato resolved, il Future non cambia mai.

Promise in JavaScript

Promise in JavaScript è analogo al Future di Dart, ma con un’API più ricca. Promise accetta un executor — una funzione con parametri resolve e reject. I metodi statici Promise.all, Promise.race, Promise.allSettled permettono di combinare più promise. Le applicazioni moderne usano la sintassi async/await sopra Promise.

Come funziona Future

Future funziona sulla base di un event loop — un ciclo di eventi che elabora operazioni asincrone senza bloccare il thread principale. Quando viene chiamata una funzione asincrona (ad esempio, http.get), restituisce immediatamente un Future. La richiesta effettiva viene eseguita in background, mentre l’event loop continua a elaborare altri eventi. Dopo il completamento dell’operazione, il Future passa allo stato resolved e then viene chiamato.

Event loop è il meccanismo chiave su cui sono costruiti Dart e JavaScript. L’event loop mantiene due code: la coda dei microtask (compiti brevi, eseguiti prima dell’evento successivo) e la coda degli eventi (eventi IO, timer). Future.then inserisce un callback nella coda dei microtask, Future.microtask — esecuzione prioritaria prima del frame successivo.

Le catene di then() permettono di elaborare sequenzialmente i risultati di operazioni asincrone senza callback hell. Ogni then() riceve il risultato del passo precedente e restituisce un nuovo Future. catchError() alla fine della catena gestisce qualsiasi errore verificatosi in qualsiasi passo. Questo rende il codice asincrono lineare e leggibile.

CompletableFuture in Java

In Java, CompletableFuture estende il concetto di Future/Promise. Il metodo supplyAsync() avvia un’attività asincrona in ForkJoinPool.commonPool(). thenApply() trasforma il risultato, thenCompose() spacchetta un CompletableFuture annidato. exceptionally() gestisce un errore restituendo un valore predefinito. allOf() e anyOf() funzionano come Promise.all e Promise.race in JavaScript. CompletableFuture supporta il completamento manuale tramite complete() e completeExceptionally() — questo è analogo a Promise resolve/reject.

Promise e il contratto di asincronia

Promise è un contratto con uno dei tre stati: pending (in attesa), fulfilled (adempiuto) o rejected (respinto). Una volta passato a fulfilled o rejected, la Promise non cambia mai — questo garantisce l’immutabilità del risultato per tutti i consumatori. Il metodo .then() viene chiamato in modo asincrono dopo essere passato a qualsiasi stato completato.

Promise.all attende il completamento di tutte le promise passate e restituisce un array di risultati nello stesso ordine. Se una promise fallisce con un errore — l’intero Promise.all passa a rejected. Promise.race restituisce il risultato della prima promise completata (successo o errore). Promise.allSettled attende tutte le promise indipendentemente dagli errori — restituisce lo stato di ciascuna.

Concatenamento è il vantaggio chiave di Promise rispetto ai callback. Ogni .then() restituisce una nuova Promise, consentendo catene di lunghezza arbitraria. Un errore in qualsiasi anello della catena si propaga fino al .catch() più vicino. Il metodo finally() esegue codice dopo il completamento della catena indipendentemente dal risultato — per la pulizia delle risorse.

Esempi di codice Future (Dart)

Il primo esempio — creare un Future tramite una funzione async e gestirlo con then/catchError:

dart
Future<String> fetchData() async {
    await Future.delayed(Duration(seconds: 1))
    return "Dati caricati"
}

fetchData()
    .then((result) => print(result))
    .catchError((error) => print("Errore: $error"))

Il secondo esempio — combinare più Future con Future.wait:

dart
Future<List<String>> loadAll() async {
    final futures = [
        fetchFromApi("/users"),
        fetchFromApi("/posts"),
        fetchFromApi("/comments")
    ]
    final results = await Future.wait(futures)
    return results
}

Il terzo esempio — Promise in JavaScript con creazione manuale e una catena then:

js
const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("Completato")
    }, 1000)
})

promise
    .then(value => console.log(value))
    .catch(err => console.error(err))

Future vs altri pattern asincroni

Future/Promise si differenzia dallo stile callback per il fatto di restituire un oggetto contratto invece di accettare una funzione callback come argomento. Lo stile callback porta all’annidamento (callback hell), mentre Future consente di costruire catene then() piatte. La sintassi async/await rende il codice Future ancora più lineare, simile al codice sincrono.

Future rappresenta esattamente un risultato asincrono — una emissione, dopo la quale il Future si completa. A differenza di Observable (Rx, ReactiveX) o Flow (Kotlin Coroutines), Future non supporta valori multipli nel tempo. Se hai bisogno di un flusso di eventi — usa Stream in Dart, Observable in Rx o Flow in Kotlin.

CompletableFuture in Java è un analogo di Promise con un’API estesa: supplyAsync, thenApply, thenCompose, exceptionally. CompletableFuture consente di combinare più operazioni asincrone tramite allOf, anyOf e permette anche di specificare un Executor personalizzato per l’esecuzione. Scala Future usa ExecutionContext per gestire il pool di thread e supporta la composizione tramite for-comprehension.

In Dart, il pattern Future differisce da Promise in JavaScript per il fatto che Dart ha una tipizzazione rigorosa: Future<String> garantisce il tipo del valore restituito. Le funzioni async in Dart devono restituire un Future e il compilatore verifica che tutti i percorsi restituiscano un tipo corretto. Future.wait è equivalente a Promise.all — prende Iterable<Future<T>> e restituisce Future<List<T>>. Future.delayed e Future.error sono factory convenienti per testare scenari asincroni.

Future/Promise è implementato in Python tramite concurrent.futures.Future e asyncio.Future. concurrent.futures.ThreadPoolExecutor.submit() restituisce un Future, su cui si può chiamare result() — una chiamata bloccante. asyncio.Future è un contenitore compatibile con await per un risultato asincrono in Asyncio. asyncio.gather() esegue più coroutine in parallelo e restituisce una lista di risultati, simile a Promise.all in JavaScript. Python utilizza anche asyncio.ensure_future basato su callback e catene add_done_callback per comporre operazioni asincrone, ma nel codice moderno viene preferita la sintassi async/await con asyncio.Task.

Nello sviluppo mobile, Future è attivamente utilizzato nelle applicazioni Flutter. Ogni richiesta di rete dal pacchetto http o dio restituisce un Future. FutureBuilder è un widget che prende un Future e uno stato snapshot: connectionDone, hasData, hasError. Quando i dati vengono ricevuti, snapshot.data contiene il risultato del Future. StreamBuilder è l’analogo per valori multipli. FutureBuilder elimina la gestione manuale dello stato di caricamento tramite setState.

Scala Future usa implicitamente ExecutionContext — il pool di thread su cui viene eseguita l’attività asincrona. La composizione tramite map, flatMap, filter funziona perché Future è una monade. for-comprehension consente di combinare sequenzialmente più Future senza annidamento: for { a <- futureA; b <- futureB } yield a + b. Future.sequence funziona come Promise.all. Il fallback in caso di errore è implementato tramite recover e recoverWith.

Future/Promise in C++ è rappresentato dalle classi std::future e std::promise della libreria <future>. std::async avvia una funzione asincrona e restituisce std::future. std::promise consente di impostare manualmente un valore tramite set_value() o un errore tramite set_exception(). A differenza di Dart e JavaScript, std::future.get() blocca il thread fino al ricevimento del risultato — questa è una differenza importante da considerare nella progettazione di applicazioni C++ multithread.

Pattern Future

Nella progettazione di API che restituiscono Future, segui la regola dell’unica fonte di verità: ogni richiesta asincrona viene creata una volta e memorizzata nella cache tramite Future.memoize. Se due componenti richiedono gli stessi dati, ricevono lo stesso Future — questo previene richieste duplicate. Per il timeout, usa Future.timeout (Dart) o Promise.race con un timeout per garantire che l’operazione asincrona venga completata entro un intervallo specificato.

Il pattern Retry in Future è implementato tramite una catena ricorsiva: in caso di errore in catch, la stessa funzione asincrona viene chiamata con un ritardo. Exponential backoff aumenta l’intervallo tra i tentativi: 1 sec — 2 sec — 4 sec — fino a max 30 sec. In Dart questo è incapsulato in un’estensione su Future: future.retry(maxRetries: 3, delay: Duration(seconds: 1)). In Scala, Future.zip combina due risultati indipendenti in una tupla — un’alternativa a Promise.all per due Future. La cancellazione non è supportata nei Future standard, ma è implementata tramite CancellationToken o AbortController in JavaScript.

Domande frequenti

Qual è la differenza tra Future e Promise?

Future è un contenitore in sola lettura per leggere un risultato asincrono tramite then o await. Promise è un contratto in sola scrittura che accetta un valore tramite resolve() e lo lega al Future. In Dart e JavaScript, questi concetti sono combinati: Future/Promise è una classe. In Java e Scala sono separati.

In cosa si differenzia Future da Observable?

Future rappresenta un singolo risultato asincrono — emette esattamente un valore e si completa. Observable (Rx) emette valori multipli nel tempo — è un flusso asincrono. Se hai bisogno di una risposta HTTP — usa Future. Se hai bisogno di un flusso di modifiche del database — usa Observable o Stream.

Cos’è async/await e come si relaziona con Future?

async/await è zucchero sintattico sopra Future/Promise. La parola chiave async marca una funzione come asincrona — restituisce un Future. await sospende l’esecuzione fino al completamento del Future, senza bloccare il thread. In Dart, JavaScript, Python e C#, async/await ha sostituito le catene then nella maggior parte degli scenari.

Come gestire gli errori in una catena Future?

In Dart, usa .catchError() alla fine della catena — cattura le eccezioni in qualsiasi fase. In JavaScript — .catch(). Dart dispone anche di try/await/catch all’interno delle funzioni async. Per il recupero dagli errori, usa .catchError() con un valore predefinito o un nuovo Future.

Cos’è Promise.all e quando usarlo?

Promise.all prende un array di promise e restituisce un’unica Promise che si risolve con un array di risultati. Tutte le promise vengono eseguite in parallelo — questo accelera le operazioni indipendenti (ad esempio, caricare più file). Se una qualsiasi promise viene respinta — l’intero Promise.all viene respinto con lo stesso errore.

Riepilogo

  • Future/Promise — un pattern di programmazione asincrona con un contratto per un singolo valore
  • Future — un contenitore in sola lettura per leggere il risultato di un’operazione
  • Promise — un contratto in sola scrittura che riempie il Future tramite resolve/reject
  • Catene then — elaborazione piatta di passaggi asincroni senza callback hell
  • async/await — zucchero sintattico su Future, rendendo il codice lineare
  • Promise.all — esecuzione parallela di più operazioni asincrone
  • Per valori multipli nel tempo, usa Stream o Observable, non Future

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche