Asynchroon programmeren in Python: van de basis tot het gebruik in de praktijk

Laatste update: 03/17/2026
Auteur: C Bronpad
  • Asynchroon programmeren in Python maakt het mogelijk dat meerdere I/O-intensieve taken tegelijkertijd worden uitgevoerd zonder elkaar te blokkeren. async, await en de gebeurtenisloop.
  • Met behulp van tools zoals asyncio, aiohttpAsynchrone contextmanagers en asynchrone iteratie maken schaalbare netwerken en API-intensieve workloads mogelijk.
  • Asynchrone verwerking is uitstekend geschikt voor netwerk- en bestands-I/O, maar moet worden aangevuld met multiprocessing of gespecialiseerde services voor CPU-intensieve taken.
  • Goede werkwijzen – zoals het vermijden van blokkerende aanroepen, het beperken van gelijktijdige uitvoering en het afhandelen van fouten per taak – zijn essentieel voor het schrijven van betrouwbare asynchrone applicaties.

asynchroon programmeren in Python

Asynchroon programmeren in Python is uitgegroeid van een nicheonderwerp tot een van de kernvaardigheden voor iedereen die moderne, responsieve applicaties bouwt. Als je werkt met web-API's, microservices, realtime dashboards of andere vormen van intensieve input/output (I/O), ben je waarschijnlijk wel eens tegen het probleem aangelopen dat je code meer tijd besteedt aan wachten dan aan daadwerkelijk werk. Dat is precies waar asynchrone technieken van pas komen.

In plaats van je programma stil te laten staan ​​in afwachting van het netwerk, de schijf of een externe service, kun je met asynchrone code die wachttijden overbruggen en de applicatie in beweging houden. In deze handleiding gaan we dieper in op hoe asynchroon programmeren werkt in Python, welke problemen het oplost, wanneer het echt nuttig is en wanneer het juist niet de juiste tool is. We zullen concrete voorbeelden bekijken waarin asynchroon programmeren wordt gebruikt. async, await, asyncio en populaire asynchrone bibliotheken zoals aiohttp.

Wat is asynchroon programmeren in Python?

In essentie is asynchroon programmeren een manier om je code zo te structureren dat meerdere taken tegelijkertijd kunnen worden uitgevoerd zonder elkaar te blokkeren, zelfs wanneer ze één enkele thread van het besturingssysteem delen. Bij de klassieke synchrone stijl is elke bewerking voltooid voordat de volgende zelfs maar begint: een API aanroepen, wachten, het antwoord verwerken en pas dan verdergaan. Met asynchrone code kun je meerdere langlopende bewerkingen starten en Python ertussen laten schakelen wanneer een van de bewerkingen vastloopt.

Python implementeert dit model met een combinatie van speciale syntax en een coöperatieve scheduler die is opgebouwd rond een gebeurtenisloop. De twee sleutelwoorden die dit alles mogelijk maken zijn: async en await: je markeert functies als asynchroon met behulp van async defen je pauzeert erin met await telkens wanneer je een bewerking tegenkomt die de controle terug kan geven aan de gebeurtenisloop.

An async def De functie retourneert geen waarde direct; ze retourneert een coroutine-object dat een berekening vertegenwoordigt die kan worden ingepland en afgewacht. Wanneer u gebruik maken van await Binnen die functie onderbreekt Python de huidige coroutine en laat andere lopende taken draaien totdat de verwachte bewerking (zoals een netwerkverzoek) is voltooid. Daarna wordt de uitvoering direct hervat. await.

Dit is cruciaal: asynchrone Python-code is meestal nog steeds single-threaded, maar gelijktijdig in de zin dat meerdere bewerkingen in overlappende tijdsvensters worden uitgevoerd. Terwijl de ene taak wacht op I/O, krijgt een andere taak CPU-tijd. Daarom is asynchroon programmeren perfect voor I/O-intensieve taken, maar versnelt het niet op magische wijze CPU-intensieve taken.

Python asyncio event loop

Een concrete analogie: schaakdemonstraties en wachttijd

Een klassieke analogie die in de Python-gemeenschap wordt gebruikt om gelijktijdige versus sequentiële uitvoering uit te leggen, komt van een simultaan schaakdemonstratie. Stel je voor dat een grootmeester tegen 24 amateurs speelt. Ze kan het toernooi op twee verschillende manieren aanpakken, waarbij ze zowel synchrone als asynchrone strategieën hanteert.

In de synchrone versie gaat ze met één tegenstander aan tafel zitten en speelt ze dat ene spel van begin tot eind, waarna ze doorgaat naar de volgende tafel. Elke zet die ze doet, duurt 5 seconden, terwijl elke amateur ongeveer 55 seconden nadenkt. Een typische partij bestaat uit 30 zetten (dus 60 zetten in totaal). Dat betekent dat elke partij (55 + 5) × 30 = 1800 seconden duurt, ongeveer 30 minuten. Met 24 partijen duurt het hele evenement 12 uur.

In de asynchrone versie loopt ze door de kamer en doet ze één zet op elk bord, waarna ze meteen doorloopt naar het volgende bord terwijl de huidige tegenstander nadenkt over zijn of haar reactie. Eén zetronde over 24 borden duurt 24 × 5 = 120 seconden, oftewel 2 minuten. Na 30 van zulke rondes is de volledige reeks spellen in ongeveer 3600 seconden voltooid, oftewel 1 uur.

De belangrijkste conclusie is dat haar pure speelsnelheid nooit veranderde; wat wel veranderde, was hoe ze de wachttijd van haar tegenstanders benutte. Asynchrone Python-code werkt volgens hetzelfde principe: het maakt I/O niet sneller, maar zorgt ervoor dat je iets nuttigs doet terwijl je anders vast zou zitten in afwachting van het netwerk, de schijf of een andere externe bron.

Synchrone versus asynchrone verzoeken: een praktijkvoorbeeld met API's

Een van de meest voorkomende toepassingen van asynchrone programmering in Python is het werken met externe API's, waarbij elke aanvraag gemakkelijk honderden milliseconden of langer kan duren. Ter illustratie: stel je voor dat je het aantal volgers van verschillende GitHub-accounts wilt ophalen met behulp van hun openbare API.

De meest voor de hand liggende synchrone aanpak zou gebruikmaken van een populaire blokkerende HTTP-client zoals requests. Je voert in een lus een GET-verzoek uit voor elk gebruikers-eindpunt, leest de JSON-payload en extraheert de followers Het veld wordt ingevuld en afgedrukt of opgeslagen. Dit is eenvoudig en leesbaar, maar heeft een nadeel: voor elk account dat wordt verwerkt, voert het programma het verzoek uit en wacht vervolgens op het antwoord voordat het aan het volgende account begint.

Dus als je drie gebruikers controleert zoals api.github.com/users/python, api.github.com/users/google en api.github.com/users/firebaseDe code verstuurt het eerste verzoek, wacht tot GitHub reageert, gaat dan verder met het tweede verzoek, enzovoort. Met een handvol gebruikers is dit misschien acceptabel, maar naarmate uw lijst groeit tot honderden of duizenden, schiet de totale verwerkingstijd omhoog, omdat uw app het grootste deel van zijn levensduur inactief is en wacht op de externe server.

Om het proces te versnellen, kunt u overschakelen naar een asynchrone implementatie die is gebouwd bovenop asyncio en een asynchroon-compatibele HTTP-client zoals aiohttp. In dat model start je meerdere coroutine-taken die vrijwel gelijktijdig hun HTTP-verzoeken versturen. De event loop wacht vervolgens op reacties van elk van deze taken en hervat elke taak zodra er gegevens binnenkomen, in plaats van te wachten tot een verzoek volledig is afgerond voordat het volgende wordt gestart.

Als je deze twee benaderingen naast elkaar vergelijkt, wint de asynchrone versie meestal met een grote marge, vooral naarmate het aantal gebruikers toeneemt. De tijd per verzoek verandert niet, maar de totale tijd om alle resultaten te verkrijgen daalt aanzienlijk omdat u veel verbindingen gelijktijdig in plaats van sequentieel verwerkt.

Kernconcepten: Coroutines, Event Loop, Taken en Futures

De moderne asynchrone Python-programmeertaal draait in feite om een ​​paar belangrijke bouwstenen, die voornamelijk worden aangeleverd door de asyncio module. Het begrijpen van deze concepten zal de rest van het ecosysteem veel minder mysterieus maken en u helpen bij het ontwerpen van robuuste asynchrone architecturen.

Een coroutine is een speciaal soort functie die zijn eigen uitvoering kan pauzeren en hervatten. In de huidige syntaxis definieer je er een met async defWanneer je deze functie aanroept, krijg je een coroutine-object dat moet worden afgewacht of ingepland; het wordt niet direct volledig uitgevoerd zoals een normale functie. Binnenin, telkens wanneer je deze functie gebruikt... await Wanneer een object op een awaitable wacht (een andere coroutine, een taak, een future, enz.), schort Python die coroutine op totdat de gewachte bewerking is voltooid.

De event loop is de coördinator die alle lopende coroutines, I/O-bewerkingen en timers bijhoudt en bepaalt welk stuk code op een bepaald moment wordt uitgevoerd. Historisch gezien moest je de lus expliciet verkrijgen en beheren via asyncio.get_event_loop()Maar in moderne Python-code is het de voorkeursmethode om te laten asyncio.run() Het programma maakt, voert uit en sluit de lus voor je rond een asynchrone functie op het hoogste niveau, zoals: main().

Taken zijn wrappers rond coroutines die de event loop opdracht geven om ze in te plannen voor uitvoering. Je kunt ze beschouwen als lichte taken: de lus kan de voortgang over meerdere taken verdelen zonder meerdere threads op te starten. Je maakt taken doorgaans aan met asyncio.create_task() of door helpers in te schakelen zoals asyncio.gather()die intern een verzameling taken beheren.

Futures vertegenwoordigen resultaten die later beschikbaar komen, vergelijkbaar met Promises in JavaScript. Zowel taken als futures zijn awaitable objecten: je kunt await Ze worden opgeschort totdat de onderliggende bewerking is voltooid. Dit uniforme protocol vereenvoudigt de orchestratiecode aanzienlijk, omdat het samenstellen van asynchrone processen neerkomt op het wachten op de juiste objecten in de juiste volgorde.

Asynchrone syntaxis in de praktijk: async, await, async with en async for

De Politia Militar hield zelfs tijdens de pre-carnaval festiviteiten de zaken al nauwlettend in de gaten. async Het sleutelwoord is niet beperkt tot functiedefinities; het is ook van toepassing op contextmanagers en lussen, zodat meer geavanceerde patronen kunnen deelnemen aan de asynchrone wereld. Kennis van deze uitgebreide syntaxis helpt je bij het schrijven van elegante code rondom netwerkverbindingen, sessies, streams en aangepaste protocollen.

De meest voorkomende vorm is async def, wat een asynchrone functie definieert (een coroutine-fabriek). Binnen een dergelijke functie zult u ruimhartig gebruikmaken van await telkens wanneer je een andere coroutine of awaitable bewerking aanroept, zoals asyncio.sleep()een asynchrone HTTP-aanvraag of een asynchrone databasequery. Houd er rekening mee dat u geen gebruik kunt maken van await direct op het hoogste niveau van een script; het moet zich binnen een bevinden async def.

Hoewel je misschien in de verleiding komt om te bellen time.sleep() Het gebruik van vertragingen binnen je coroutine zou het hele doel van asynchroon programmeren tenietdoen. time.sleep() Dit blokkeert de hele thread, inclusief de event loop, waardoor geen enkele andere asynchrone taak gedurende die tijd kan worden uitgevoerd. In plaats daarvan moet u de niet-blokkerende tegenhanger gebruiken. asyncio.sleep(), waardoor de controle teruggegeven wordt aan de lus terwijl de timer aftelt.

Python ondersteunt ook asynchrone contextmanagers via async with, geïmplementeerd door het definiëren van de speciale methoden __aenter__ en __aexit__. Dit is met name handig bij het werken met objecten die een nette opzet- en afsluitprocedure vereisen met asynchrone bewerkingen, zoals het openen van een netwerksessie of het verkrijgen van een asynchrone bron. Een typisch voorbeeld is het beheren van een aiohttp.ClientSession of een individuele HTTP-aanvraag met behulp van async with blokken in plaats van handmatig aanroepen close().

Ten slotte wordt asynchrone iteratie beschikbaar gesteld via async for, die gebaseerd is op magische methoden __aiter__ en __anext__ beschreven in PEP 492. Met asynchrone iterators en asynchrone generators kun je items in de loop van de tijd vrijgeven. await Binnen het iteratieproces is dit perfect voor het streamen van gegevens die geleidelijk via het netwerk of vanuit een andere asynchrone bron binnenkomen.

Meerdere taken tegelijk uitvoeren met asyncio

De ware kracht van asynchroon programmeren komt pas tot uiting wanneer je meerdere I/O-intensieve taken gelijktijdig uitvoert in plaats van na elkaar. In het asynchrone ecosysteem van Python zijn de belangrijkste tools daarvoor: asyncio.create_task() en asyncio.gather()die beide coroutines inplannen op de event loop.

Met asyncio.gather()Je kunt meerdere coroutines tegelijk starten en wachten tot ze allemaal klaar zijn. Je ontvangt de resultaten vervolgens als een lijst of tuple. Dit komt extreem vaak voor bij batches HTTP-aanroepen, databasequery's of andere herhaalde asynchrone bewerkingen. Achter de schermen... gather() Het verpakt elke coroutine in een taak en zorgt ervoor dat ze allemaal worden voltooid.

Als je teruggaat naar het voorbeeld van het ophalen van GitHub-profielen, maar het herschrijft met behulp van aiohttp en asyncio.gather()Uiteindelijk krijg je dan drie aanroepen naar een functie zoals fetch_user() worden gelijktijdig gelanceerd. Elke taak start zijn HTTP-verzoek, geeft de controle over in afwachting van de gegevens en hervat vervolgens de verwerking van het antwoord zodra dit binnenkomt. Vanuit het perspectief van de gebruiker verschijnen alle drie de resultaten ongeveer gelijktijdig.

Er zijn echter situaties waarin je niet duizenden of miljoenen taken tegelijk wilt uitvoeren, omdat dit je eigen computer kan overbelasten of externe limieten kan overschrijden. Een veelvoorkomend patroon is om de gelijktijdige verwerking te beperken door slechts een beperkt aantal processen tegelijk te verwerken. MAX_TASKS bewerkingen tegelijk uitvoeren, met behulp van semaforen, afgebakende pools of handmatige batchverwerking binnen uw asynchrone workflow.

Een ander cruciaal aspect bij het gelijktijdig uitvoeren van veel taken is de manier waarop je met fouten omgaat; het is in de praktijk zelden acceptabel dat één enkele mislukte aanvraag de hele batch laat crashen. Idealiter zou uw asynchrone orchestratie uitzonderingen per taak moeten opvangen en afhandelen, bijvoorbeeld door ze te loggen, selectief opnieuw te proberen of gedeeltelijke resultaten terug te geven terwijl de rest van de batch intact blijft.

Omgaan met gelijktijdige processen: voordelen en valkuilen

Het is belangrijk om de begrippen gelijktijdigheid en parallellisme van elkaar te onderscheiden, omdat asynchrone Python weliswaar het eerste biedt, maar niet per se het tweede. Gelijktijdigheid betekent dat meerdere taken tegelijkertijd voortgang boeken met overlappende intervallen, terwijl parallellisme inhoudt dat ze letterlijk op hetzelfde moment op meerdere CPU-kernen worden uitgevoerd.

Typische asynchrone code die gebruikmaakt van asyncio creëert geen meerdere OS-threads; in plaats daarvan multiplexeert het taken in één enkele thread op basis van het moment waarop elke taak geblokkeerd is door I/O, vergelijkbaar met programma asíncrona in Node.js. Daarom schaalt het zo goed met duizenden verbindingen: contextwisseling is goedkoop omdat het coöperatief is en wordt aangestuurd door de event loop in plaats van door het besturingssysteem.

Dit ontwerp brengt uitdagingen met zich mee, met name op het gebied van coördinatie en foutafhandeling. Omdat je logica nu verspreid is over meerdere coroutines die in de tijd door elkaar lopen, moet je bewuster omgaan met het delen van de status, het doorgeven van fouten en het opruimen van resources. Bugs zoals vergeten awaitTaken die nooit worden afgewacht, of uitzonderingen die stilletjes worden genegeerd in achtergrondtaken, kunnen subtiel zijn en moeilijk op te sporen.

Om je asynchrone codebase onderhoudbaar te houden, moet je degelijke engineeringpraktijken volgen: zorg ervoor dat coroutines zich richten op één enkele verantwoordelijkheid, centraliseer foutafhandeling waar mogelijk en voeg voldoende logging toe om te begrijpen wat er tijdens de uitvoering gebeurt. Goede tools en duidelijke conventies dragen in grote mate bij aan het voorkomen van problemen zoals race-condities of resourcelekken, zelfs in een asynchrone omgeving met één thread.

Wanneer asynchrone code echt nuttig is (en wanneer niet)

Asynchroon programmeren is ontzettend effectief voor I/O-intensieve workloads, maar het is geen wondermiddel voor elk prestatieprobleem. De eerste stap bij elke optimalisatiepoging is vaststellen of de knelpunten te wijten zijn aan I/O of aan CPU-intensieve berekeningen.

Als uw applicatie het grootste deel van de tijd wacht op netwerkreacties, bestanden leest en schrijft, databases opvraagt ​​of communiceert via sockets, dan is asynchroon vrijwel zeker een goede keuze. Typische voorbeelden zijn web-API's die communiceren met meerdere externe services, ETL-pipelines die gelijktijdig gegevens uit verschillende bronnen lezen en ernaar schrijven, en microservices die veel gelijktijdige clientverbindingen onderhouden.

Aan de andere kant, als uw werklast voornamelijk bestaat uit zware CPU-bewerkingen zoals numerieke berekeningen, beeldverwerking of complexe simulaties, zal asynchroon alleen de snelheid niet verhogen. In dergelijke scenario's beperkt de GIL (Global Interpreter Lock) nog steeds wat er parallel kan worden uitgevoerd binnen één Python-proces. Meestal behaalt u betere resultaten met multiprocessing, native extensies of door gebruik te maken van gespecialiseerde backends.

In bedrijfsomgevingen is een pragmatische strategie om deze technieken te combineren: gebruik asyncio en asynchroon-compatibele SDK's voor cloudservices (AWS, Azure en andere) om de latentie te minimaliseren en de doorvoer te maximaliseren, terwijl CPU-intensief werk wordt gedelegeerd aan aparte processen, workers of beheerde computerdiensten. Op die manier benut je de sterke punten van elk hulpmiddel in plaats van tegen de runtime van de programmeertaal te vechten.

Beste werkwijzen voor het schrijven van asynchrone Python-code

Als je asynchroon programmeren op grotere schaal gaat toepassen, zullen bepaalde patronen en gewoonten je helpen de meest voorkomende valkuilen te vermijden. Ze maken je code ook duidelijker voor teamleden die misschien nog niet zo bekend zijn met het asynchrone ecosysteem.

Een fundamentele regel is om blokkerende aanroepen in je asynchrone code te vermijden. Dat betekent dat je dingen moet vervangen zoals time.sleep() with await asyncio.sleep()En wees voorzichtig met bibliotheken die geen asynchroon-compatibele API's bieden. Als een pakket van een derde partij puur synchroon is, kan het veelvuldig aanroepen ervan vanuit een coroutine de gebeurtenisloop blokkeren en de voordelen van gelijktijdigheid tenietdoen.

Wanneer u een reeks onafhankelijke I/O-bewerkingen hebt, is het raadzaam deze gelijktijdig uit te voeren met behulp van hulpprogramma's zoals asyncio.gather() of groepen taken die beperkt worden door een maximaal gelijktijdigheidsniveau. Dit patroon verhoogt de doorvoer en behoudt tegelijkertijd de controle over het aantal open verbindingen of lopende verzoeken.

Als ontwerprichtlijn is het aan te raden coroutines relatief klein te houden en te focussen op een duidelijke taak, vergelijkbaar met hoe je functies zou ontwerpen in schone, synchrone code. Grote, monolithische coroutines die netwerken, bedrijfslogica en foutafhandeling combineren, worden al snel moeilijk te testen en te doorgronden, vooral wanneer er halverwege iets misgaat.

Controleer tot slot altijd of de ecosysteemcomponenten waarop u vertrouwt daadwerkelijk asynchroon gebruik ondersteunen. Veel populaire bibliotheken bieden aparte asynchrone clients of speciale submodules; andere kunnen echter nog steeds blokkerende functies bevatten, zelfs als ze adverteren met "asynchrone" mogelijkheden. Door de documentatie zorgvuldig te lezen en kleine benchmarks uit te voeren, kunt u subtiele prestatieverminderingen voorkomen.

Praktische gebruiksscenario's en architectuurideeën

In softwareprojecten in de praktijk komt asynchroon programmeren goed tot zijn recht in uiteenlopende architecturen, van traditionele webbackends tot geavanceerde, door AI aangedreven systemen. De verbindende factor is altijd de noodzaak om veel I/O-intensieve bewerkingen af ​​te handelen zonder tijd te verspillen aan inactief wachten.

Een klassiek scenario is een webservice die meerdere externe API's moet aanroepen om één enkel antwoord voor de client te genereren. Door asynchroon te werken, kan de service alle uitgaande verzoeken tegelijk versturen en de uiteindelijke gegevens samenstellen zodra elk onderdeel binnenkomt. Dit verkort de totale responstijd aanzienlijk. Dit is gebruikelijk in microservice-architecturen en integraties met betaalproviders, sociale netwerken of analyseplatformen.

Een ander belangrijk toepassingsgebied is data-engineering: pipelines en ETL-taken werken vaak parallel met meerdere databases, bestandssystemen of cloudopslagbuckets. Door gelijktijdig gegevens uit meerdere bronnen te lezen en de resultaten direct weg te schrijven, verlaag je de algehele latentie en maak je beter gebruik van de beschikbare bandbreedte, met name bij het werken met cloudopslag of REST-gebaseerde data-API's.

Async werkt ook prima samen met business intelligence-dashboards en -tools zoals Power BI, waar backends gegevens van verschillende services moeten samenvoegen zonder langlopende HTTP-verbindingen te blokkeren. Bouw je eigen API-lagen of integratie-microservices met asyncio Kan de waargenomen reactiesnelheid en doorvoer onder belasting verbeteren.

Bedrijven die gespecialiseerd zijn in maatwerksoftware, kunstmatige intelligentie, cybersecurity en cloudconsultancy maken vaak veelvuldig gebruik van asynchrone technieken om workflows te orkestreren die AI-modellen aanroepen, gebeurtenissen registreren, bedreigingen monitoren en communiceren met cloudcontroleplatformen. Het combineren van asynchrone I/O voor orkestratie met aparte, voor de CPU geoptimaliseerde workers voor het zware werk is een veelvoorkomend intern patroon dat schaalbare en onderhoudbare systemen oplevert.

Voor veel ontwikkelaars en teams is de eerste stap simpelweg het introduceren van asynchrone programmering in de delen van de applicatie die duidelijk "I/O-intensief" zijn, en vervolgens van daaruit verder te gaan naarmate de voordelen duidelijk worden en het team meer vertrouwen krijgt in de paradigma's en tools.

Uiteindelijk draait asynchroon programmeren in Python om het verstandig gebruiken van wachttijd: door je code zo te structureren dat deze efficiënt werkt. async, awaitMet coroutines en de event loop kun je applicaties bouwen die sneller aanvoelen, beter schalen onder belasting en optimaal gebruikmaken van beschikbare resources, vooral bij de omgang met netwerken, bestanden en externe services.

tutorial van Node.js voor principes
Gerelateerd artikel:
Tutorial van Node.js voor principes: de basis van uw app
Gerelateerde berichten: