La programmazione asincrona è stata introdotta in Python a partire dalla versione 3.5 con l'introduzione delle parole chiave async e await. Inizialmente, per le attività asincrone venivano utilizzate librerie come asyncio e generatori basati su coroutine, che erano difficili da comprendere e mantenere. Con l'arrivo della sintassi async/await, il codice asincrono è diventato più esplicito e leggibile, avvicinandosi allo stile sincrono consueto.
Prima dell'introduzione di async/await, l'asincronicità era implementata tramite callback e generatori (ad esempio, utilizzando la libreria tornado o le vecchie API asyncio). Questo codice era difficile da debug e mantenere.
Il problema principale nella gestione di un gran numero di operazioni I/O simultanee (richieste di rete, input/output di file) nel codice sincrono è il blocco del thread principale. Questo porta a una diminuzione delle prestazioni e all'impossibilità di utilizzare efficientemente le risorse.
La programmazione asincrona utilizzando async/await consente di eseguire molte operazioni di input e output parallelamente all'interno di un singolo thread, evitando blocchi. Inoltre, la sintassi è simile alle comuni funzioni, il che facilita la leggibilità e il debug.
import asyncio async def fetch_data(delay): print(f"Inizio del fetching dopo {delay}s di ritardo") await asyncio.sleep(delay) print(f"Fetch completato dopo {delay}s di ritardo") return delay async def main(): results = await asyncio.gather( fetch_data(1), fetch_data(2), fetch_data(3) ) print("Risultati:", results) asyncio.run(main())
Una funzione definita con async def può essere chiamata come una funzione normale?
No. La chiamata a tale funzione restituisce un oggetto coroutine, che non viene eseguito fino a quando non viene passato all'event loop (ad esempio, utilizzando await o asyncio.run()).
def foo(): return 42 async def bar(): return 42 print(foo()) # 42 print(bar()) # <coroutine object bar at ...>
È possibile utilizzare await al di fuori di una funzione asincrona?
No. La parola chiave await deve essere utilizzata solo all'interno di funzioni dichiarate con async def. Tentare di utilizzare await al di fuori di tale funzione genererà un SyntaxError.
# Errore! await asyncio.sleep(1) # SyntaxError: 'await' outside async function
L'asincronicità funziona per operazioni non legate a I/O (ad esempio, computazioni)?
No. L'asincronicità è efficiente solo per operazioni di input/output. Per le attività computazionali è ancora necessario multiprocessing o threading, altrimenti l'event loop sarà bloccato.
Vantaggi:
Svantaggi:
Caso negativo: Sviluppatori giovani hanno cercato di velocizzare l'applicazione usando async/await, ma hanno effettuato operazioni di calcolo in modo asincrono piuttosto che richieste di rete. L'applicazione non è accelerata. Vantaggi: si sono familiarizzati con la sintassi. Svantaggi: nessun profitto, codice complicato.
Caso positivo: Hanno gestito in modo asincrono migliaia di richieste API. Il server è riuscito a gestire più clienti senza aumentare le risorse. Vantaggi: notevole aumento delle prestazioni, architettura semplificata. Svantaggi: aumento della soglia di ingresso per i principianti.