Programació asíncrona en Python: dels conceptes bàsics a l'ús real

Darrera actualització: 03/17/2026
  • La programació asíncrona en Python permet que diverses tasques vinculades a E/S progressin sense bloquejar-se mútuament mitjançant async, await i el bucle d'esdeveniments.
  • Utilitzant eines com asyncio, aiohttp, els gestors de context asíncrons i la iteració asíncrona permeten xarxes escalables i càrregues de treball amb moltes API.
  • L'async destaca per a l'E/S de xarxa i fitxers, però s'hauria de complementar amb multiprocessament o serveis especialitzats per a tasques dedicades a la CPU.
  • Les bones pràctiques (evitar el bloqueig de trucades, limitar la concurrència i gestionar els errors per tasca) són clau per escriure aplicacions asíncrones fiables.

programació asíncrona en Python

La programació asíncrona en Python ha passat de ser un tema de nínxol a una de les habilitats bàsiques per a qualsevol persona que creï aplicacions modernes i responsives. Si treballeu amb API web, microserveis, quadres de comandament en temps real o qualsevol tipus d'entrada/sortida (E/S) pesada, probablement heu arribat a aquell punt on el vostre codi passa més temps esperant que fent feina real. Aquí és exactament on destaquen les tècniques asíncrones.

En lloc de deixar que el programa estigui inactiu mentre espera la xarxa, el disc o un servei extern, el codi asíncron permet superposar aquests períodes d'espera i mantenir l'aplicació en moviment. En aquesta guia, aprofundirem en com funciona l'async a Python, quins problemes resol, quan realment ajuda i quan és l'eina equivocada, i repassarem exemples concrets utilitzant... async, await, asyncio i biblioteques asíncrones populars com ara aiohttp.

Què és la programació asíncrona en Python?

En essència, la programació asíncrona és una manera d'estructurar el codi de manera que diverses tasques puguin progressar sense bloquejar-se mútuament, fins i tot quan comparteixen un únic fil del sistema operatiu. En l'estil síncron clàssic, cada operació acaba abans que comenci la següent: crida una API, espera, analitza la resposta i només llavors continua. Amb codi asíncron, pots activar diverses operacions de llarga durada i deixar que Python canviï entre elles sempre que una d'elles estigui esperant.

Python implementa aquest model amb una combinació de sintaxi especial i un planificador cooperatiu construït al voltant d'un bucle d'esdeveniments. Les dues paraules clau que desbloquegen tot això són async i await: marqueu les funcions com a asíncrones utilitzant async def, i fas una pausa dins d'ells amb await sempre que executeu una operació que pot retornar el control al bucle d'esdeveniments.

An async def La funció no retorna un valor directament; retorna un objecte coroutina que representa un càlcul que es pot programar i esperar. Quan utilitzeu await Dins d'aquesta funció, Python suspèn la corrutina actual i deixa que altres tasques pendents s'executin fins que l'operació esperada (com una sol·licitud de xarxa) es completi, moment en què l'execució es reprèn just després de await.

Això és crucial: el codi Python asíncron normalment encara és d'un sol fil, però concurrent en el sentit que diverses operacions avancen en finestres de temps superposades. Mentre una tasca espera E/S, una altra tasca rep temps de CPU. És per això que l'async és perfecte per a càrregues de treball amb E/S, però no accelera màgicament el treball que requereix molta CPU.

bucle d'esdeveniments asíncrona de Python

Una analogia concreta: exhibicions d'escacs i temps d'espera

Una analogia clàssica utilitzada a la comunitat Python per explicar la concurrència versus l'execució seqüencial prové d'una exhibició d'escacs simultània. Imagineu-vos una gran mestra jugant contra 24 aficionats. Pot organitzar l'esdeveniment de dues maneres diferents, imitant estratègies síncrones i asíncrones.

En la versió síncrona, s'asseu amb un oponent i juga aquesta única partida de principi a fi abans de passar a la següent taula. Cada moviment que fa triga 5 segons, mentre que cada aficionat passa uns 55 segons pensant. Una partida típica té 30 intercanvis de moviments (és a dir, 60 moviments en total). Això vol dir que cada partida dura (55 + 5) × 30 = 1800 segons, uns 30 minuts. Amb 24 partides, tot l'esdeveniment s'allarga durant 12 hores.

En la versió asíncrona, camina per l'habitació i fa un moviment a cada tauler, i immediatament camina al següent mentre l'oponent actual pensa en la seva resposta. Una ronda de moviments sobre 24 taulers triga 24 × 5 = 120 segons, o 2 minuts. Després de 30 rondes d'aquest tipus, el conjunt complet de partides es completa en aproximadament 3600 segons, és a dir, 1 hora.

La conclusió important és que la seva velocitat de joc en brut mai va canviar; el que va canviar va ser com va aprofitar el temps d'espera dels oponents. El codi Python asíncron segueix el mateix principi: no fa que l'E/S sigui més ràpida, però assegura que esteu fent alguna cosa útil mentre que altrament estaríeu atrapats esperant la xarxa, el disc o qualsevol recurs extern.

Sol·licituds sincronitzades vs. asíncrones: exemple del món real amb API

Un dels casos d'ús més comuns de l'asincronisme a Python és el fet de tractar amb API externes, on cada sol·licitud pot trigar fàcilment centenars de mil·lisegons o més. Per il·lustrar-ho, imagineu que voleu obtenir el nombre de seguidors de diversos comptes de GitHub utilitzant la seva API pública.

L'enfocament síncron senzill utilitzaria un client HTTP de bloqueig popular com ara requests. Realitzaríeu una sol·licitud GET per a cada punt final d'usuari en un bucle, llegiríeu la càrrega útil JSON, extraurieu el followers camp i imprimir-lo o emmagatzemar-lo. Això és senzill i llegible, però té un inconvenient: per a cada compte que processeu, el programa realitza la sol·licitud i després simplement espera la resposta abans de començar el següent.

Així doncs, si comproveu tres usuaris com ara api.github.com/users/python, api.github.com/users/google i api.github.com/users/firebase, el codi envia la primera sol·licitud, es bloqueja fins que GitHub respon, després passa a la segona sol·licitud, i així successivament. Amb un grapat d'usuaris això pot ser acceptable, però a mesura que la llista creix fins a centenars o milers, el temps de processament total augmenta, perquè l'aplicació passa la major part de la seva vida útil inactiva, esperant el servidor remot.

Per accelerar les coses, podeu canviar a una implementació asíncrona basada en asyncio i un client HTTP amb capacitat asíncrona com ara aiohttp. En aquest model, s'inicien diverses tasques de coroutines que activen les seves sol·licituds HTTP gairebé alhora. El bucle d'esdeveniments espera respostes de qualsevol d'elles i repren cada tasca a mesura que arriben les dades, en lloc d'esperar que una sol·licitud es completi completament abans de començar la següent.

Quan es comparen aquests dos enfocaments alhora, la versió asíncrona sol guanyar per un ampli marge, sobretot a mesura que augmenta el nombre d'usuaris. El temps per sol·licitud no canvia, però el temps total per obtenir tots els resultats disminueix dràsticament perquè gestioneu moltes connexions simultàniament en lloc de en sèrie.

Conceptes bàsics: coroutines, bucle d'esdeveniments, tasques i futurs

Sota el capó, el Python asíncron modern gira al voltant d'uns quants blocs de construcció clau proporcionats principalment per asyncio mòdul Entendre aquests conceptes farà que la resta de l'ecosistema sigui molt menys misteriós i us ajudarà a dissenyar arquitectures asíncrones robustes.

Una corrutina és un tipus especial de funció que pot pausar i reprendre la seva pròpia execució. En la sintaxi actual, es defineix un amb async defQuan la crides, obtens un objecte corutina que cal esperar o programar; no s'executa fins a la seva finalització immediatament com una funció normal. A dins, sempre que utilitzes await en una opció awaitable (una altra corrutina, una tasca, un futur, etc.), Python suspèn aquesta corrutina fins que l'operació esperada s'hagi acabat.

El bucle d'esdeveniments és l'orquestrador que fa un seguiment de totes les coroutines pendents, les operacions d'E/S i els temporitzadors, i decideix quin tros de codi s'executa en un moment donat. Històricament, calia obtenir i gestionar el bucle explícitament mitjançant asyncio.get_event_loop(), però en el codi Python modern el patró preferit és deixar asyncio.run() crear, executar i tancar el bucle al voltant d'una funció asíncrona de nivell superior com ara main().

Les tasques són embolcalls al voltant de coroutines que indiquen al bucle d'esdeveniments que les planifiqui per a la seva execució. Podeu pensar-hi com a tasques lleugeres: el bucle pot intercalar el progrés entre moltes tasques sense generar múltiples fils d'execució. Normalment creeu tasques amb asyncio.create_task() o trucant a ajudants com ara asyncio.gather(), que gestionen internament un conjunt de tasques.

Els futurs representen resultats que estaran disponibles més endavant, de manera similar a les promeses en JavaScript. Tant les tasques com els futurs són objectes esperables: podeu await que suspenguin fins que finalitzi l'operació subjacent. Aquest protocol unificat simplifica molt el codi d'orquestració, perquè la composició de fluxos asíncrons es redueix a esperar els objectes correctes en l'ordre correcte.

Sintaxi asíncrona a la pràctica: async, await, async with i async for

La async La paraula clau no es limita a les definicions de funcions; també s'estén als gestors de context i als bucles, de manera que patrons més avançats poden participar en el món asíncron. Conèixer aquesta sintaxi ampliada us ajuda a escriure codi elegant al voltant de connexions de xarxa, sessions, fluxos i protocols personalitzats.

La forma més comuna és async def, que defineix una funció asíncrona (una fàbrica de coroutines). Dins d'una funció d'aquest tipus, utilitzaràs liberalment await sempre que crideu una altra corutina o operació esperable, com ara asyncio.sleep(), una sol·licitud HTTP asíncrona o una consulta de base de dades asíncrona. Tingueu en compte que no podeu utilitzar await directament al nivell superior d'un script; ha de viure dins d'un async def.

Tot i que potser tingueu la temptació de trucar time.sleep() dins de la vostra coroutina per a retards, això frustraria completament el propòsit d'utilitzar asíncron. time.sleep() bloqueja tot el fil, inclòs el bucle d'esdeveniments, de manera que cap altra tasca asíncrona pot progressar durant aquest temps. En comptes d'això, heu d'utilitzar la contrapart no bloquejant asyncio.sleep(), que torna el control al bucle mentre el temporitzador fa el compte enrere.

Python també admet gestors de context asíncrons mitjançant async with, implementat definint els mètodes especials __aenter__ i __aexit__. Això és particularment útil quan es treballa amb objectes que necessiten una seqüència de configuració i desmuntatge neta que implica operacions asíncrones, com ara obrir una sessió de xarxa o adquirir un recurs asíncron. Un exemple típic és la gestió d'un aiohttp.ClientSession o una sol·licitud HTTP individual utilitzant async with blocs en comptes de cridar manualment close().

Finalment, la iteració asíncrona s'exposa a través de async for, que es basa en mètodes màgics __aiter__ i __anext__ descrit a PEP 492. Els iteradors asíncrons i els generadors asíncrons permeten produir elements al llarg del temps utilitzant await dins del procés d'iteració, que és perfecte per a la transmissió de dades que arriben gradualment a través de la xarxa o des d'una altra font asíncrona.

Executar diverses tasques simultàniament amb asyncio

El veritable poder de la programació asíncrona es manifesta quan s'executen diverses tasques d'E/S simultàniament en lloc d'una rere l'altra. A l'ecosistema asíncron de Python, les principals eines per a això són asyncio.create_task() i asyncio.gather(), les quals planifiquen coroutines al bucle d'esdeveniments.

Amb asyncio.gather(), podeu iniciar diverses coroutines alhora i esperar fins que totes s'hagin acabat, rebent els seus resultats com una llista o una tupla. Això és extremadament comú amb lots de crides HTTP, consultes de bases de dades o qualsevol operació asíncrona repetida. Sota el capó, gather() encapsula cada corutina en una tasca i assegura que totes es completin.

Si torneu a l'exemple d'obtenir perfils de GitHub però el refactoritzeu utilitzant aiohttp i asyncio.gather(), acabaràs amb tres crides a una funció com ara fetch_user() s'està llançant simultàniament. Cada tasca inicia la seva sol·licitud HTTP, cedeix el control mentre espera dades i després reprèn l'anàlisi de la resposta quan arriba. Des de la perspectiva de l'usuari, els tres resultats apareixen aproximadament alhora.

Tanmateix, hi ha casos en què no voleu executar milers o milions de tasques alhora, perquè això podria sobrecarregar la vostra pròpia màquina o arribar a límits de velocitat externs. Un patró comú és limitar la concurrència només processant MAX_TASKS operacions alhora, ja sigui mitjançant semàfors, grups delimitats o lògica de processament per lots manual dins del flux de treball asíncron.

Un altre aspecte crucial quan s'executen moltes tasques simultàniament és com es gestionen els errors; deixar que una sola sol·licitud fallida faci fallar tot el lot rarament és acceptable en aplicacions reals. Idealment, la vostra orquestració asíncrona hauria de detectar i gestionar excepcions per tasca, potser registrant-les, reintentant-les selectivament o retornant resultats parcials mentre manté la resta del lot intacte.

Gestió de la concurrència: avantatges i inconvenients

És important separar mentalment les idees de concurrència i paral·lelisme, perquè Python asíncron ofereix la primera però no necessàriament la segona. La concurrència significa que diverses tasques progressen en intervals superposats, mentre que el paral·lelisme implica que s'executen literalment al mateix instant en diversos nuclis de CPU.

Codi asíncron típic que utilitza asyncio no crea múltiples fils del sistema operatiu; en canvi, multiplexa les tasques en un sol fil segons quan cadascun està bloquejat a les E/S, de manera similar a programació asíncrona en Node.js. És per això que s'escala tan bé amb milers de connexions: el canvi de context és barat perquè és cooperatiu i està controlat pel bucle d'esdeveniments en lloc del sistema operatiu.

Aquest disseny presenta reptes, especialment pel que fa a la coordinació i la gestió d'excepcions. Com que la vostra lògica ara està repartida per diverses coroutines que s'intercalen en el temps, heu de ser més deliberats a l'hora de compartir l'estat, propagar errors i netejar recursos. Errors com ara oblidats await, les tasques que mai s'esperen o les excepcions que s'ingereixen silenciosament en tasques en segon pla poden ser subtils i difícils de depurar.

Per mantenir la base de codi asíncrona mantenible, heu de seguir pràctiques d'enginyeria sòlides: mantenir les coroutines centrades en una única responsabilitat, centralitzar la gestió d'errors sempre que sigui possible i afegir un registre adequat per entendre què passa en temps d'execució. Les bones eines i les convencions clares contribueixen en gran mesura a prevenir problemes semblants a les condicions de carrera o les fuites de recursos, fins i tot en un entorn asíncron d'un sol fil.

Quan el codi asíncron realment ajuda (i quan no)

La programació asíncrona és increïblement efectiva per a càrregues de treball amb E/S, però no és la solució definitiva per a tots els problemes de rendiment. El primer pas en qualsevol esforç d'optimització hauria de ser identificar si els colls d'ampolla provenen d'E/S o de càlculs limitats a la CPU.

Si la vostra aplicació passa la major part del temps esperant respostes de la xarxa, llegint i escrivint fitxers, consultant bases de dades o comunicant-se a través de sockets, aleshores l'async és gairebé segur que és una bona opció. Alguns exemples típics són les API web que es comuniquen amb diversos serveis externs, els pipelines ETL que llegeixen i escriuen a diverses fonts de dades simultàniament i els microserveis que mantenen moltes connexions de clients simultànies.

D'altra banda, si la càrrega de treball està dominada per operacions pesades de CPU com ara càlculs numèrics, processament d'imatges o simulacions complexes, l'asincronisme per si sol no accelerarà les coses. En aquests escenaris, el GIL (Global Interpreter Lock) encara limita el que es pot executar en paral·lel dins d'un únic procés de Python. Normalment obtindreu millors resultats amb multiprocessament, extensions natives o aprofitant backends especialitzats.

En entorns corporatius, una estratègia pragmàtica és combinar aquestes tècniques: utilitzar SDK asíncrons i compatibles amb asíncronisme per a serveis al núvol (AWS, Azure i altres) per minimitzar la latència i maximitzar el rendiment, alhora que delegar el treball que requereix molta CPU a processos separats, treballadors o serveis de computació gestionats. D'aquesta manera, s'exploten els punts forts de cada eina en comptes de lluitar contra el temps d'execució del llenguatge.

Millors pràctiques per escriure Python asíncron

Un cop comenceu a adoptar l'asincronisme de manera més àmplia, certs patrons i hàbits us ajudaran a evitar els errors més comuns. També fan que el vostre codi sigui més clar per als companys d'equip que potser encara no estiguin gaire familiaritzats amb l'ecosistema asíncron.

Una regla fonamental és evitar bloquejar les trucades a les rutes de codi asíncrones. Això vol dir substituir coses com ara time.sleep() amb await asyncio.sleep(), i anar amb compte amb les biblioteques que no ofereixen API compatibles amb asíncrones. Si un paquet de tercers és purament síncron, cridar-lo extensament des d'una corutina pot bloquejar el bucle d'esdeveniments i arruïnar els beneficis de la concurrència.

Sempre que tingueu un lot d'operacions d'E/S independents, preferiu executar-les simultàniament mitjançant utilitats com ara asyncio.gather() o grups de tasques restringides per un nivell màxim de concurrència. Aquest patró augmenta el rendiment alhora que manté el control sobre el nombre de connexions obertes o sol·licituds en vol.

Com a guia de disseny, intenteu mantenir les coroutines relativament petites i centrades en una responsabilitat clara, de manera similar a com dissenyarieu funcions en codi síncron net. Les grans coroutines monolítiques que combinen xarxes, lògica empresarial i gestió d'errors es tornen ràpidament difícils de provar i raonar, sobretot quan alguna cosa falla a mig camí.

Finalment, comproveu sempre si els components de l'ecosistema en què confieu realment admeten l'ús asíncron. Moltes biblioteques populars proporcionen clients asíncrons separats o submòduls dedicats; d'altres encara poden estar bloquejant sota el capó, fins i tot si anuncien funcions "asíncrones". Llegir la documentació amb atenció i fer petites proves comparatives us pot estalviar regressions subtils de rendiment.

Escenaris d'ús pràctics i idees d'arquitectura

En projectes de programari del món real, l'async brilla en una varietat d'arquitectures, des de backends web tradicionals fins a sistemes d'avantguarda basats en IA. L'element unificador sempre és la necessitat de gestionar moltes operacions d'E/S sense perdre temps en espera inactiva.

Un escenari clàssic és un servei web que necessita cridar diverses API externes per crear una única resposta per al client. Mitjançant l'asincronisme, el servei pot activar totes les sol·licituds sortints alhora i muntar la càrrega útil final tan bon punt arriba cada peça, reduint significativament el temps de resposta total. Això és habitual en arquitectures de microserveis i integracions amb passarelles de pagament, xarxes socials o plataformes d'anàlisi.

Un altre cas d'ús important és l'enginyeria de dades: els pipelines i les tasques ETL interactuen freqüentment amb múltiples bases de dades, sistemes de fitxers o contenidors d'emmagatzematge al núvol en paral·lel. En llegir de diverses fonts simultàniament i escriure els resultats tan bon punt estiguin a punt, es redueix la latència general i es fa un millor ús de l'amplada de banda disponible, especialment quan es treballa amb emmagatzematge al núvol o API de dades basades en REST.

L'async també funciona bé amb quadres de comandament d'intel·ligència empresarial i eines com el Power BI, on els backends han d'agregar dades de diferents serveis sense bloquejar les connexions HTTP de llarga durada. Creació de capes d'API personalitzades o microserveis d'integració amb asyncio pot millorar la capacitat de resposta percebuda i el rendiment sota càrrega.

Les empreses especialitzades en programari personalitzat, intel·ligència artificial, ciberseguretat i consultoria al núvol sovint depenen en gran mesura de tècniques asíncrones per orquestrar fluxos de treball que criden models d'IA, registren esdeveniments, monitoritzen amenaces i comuniquen amb plans de control al núvol. La combinació d'E/S asíncrones per a l'orquestració amb treballadors separats optimitzats per CPU per a la feina pesada és un patró intern comú que produeix sistemes escalables i mantenibles.

Per a molts desenvolupadors i equips, el primer pas és simplement introduir l'asincronia a les parts de l'aplicació que clarament indiquen "lligats a E/S", i després iterar a partir d'aquí a mesura que els beneficis es fan evidents i l'equip guanya confiança amb els paradigmes i les eines.

En definitiva, la programació asíncrona en Python consisteix a utilitzar el temps d'espera amb prudència: estructurant el codi al voltant de async, await, coroutines i el bucle d'esdeveniments, podeu crear aplicacions que semblin més ràpides, escalin millor sota càrrega i aprofitin al màxim els recursos disponibles, especialment quan es tracta de xarxes, fitxers i serveis externs.

tutorial de Node.js per a principiants
Article relacionat:
Tutorial de Node.js per a principiants: de zero a la teva primera app
Articles Relacionats: