Een voorproefje van async
Draai een coroutine
Les 1 eindigde met een regel: een handler op een async def en await, en draait een coroutine met asyncio.run, op de server, waar je het kunt zien gebeuren.
async def maakt een coroutinefunctie
async def label(snapshot):
return f"version {snapshot['version']}"
Het enige nieuwe woord is async, en het verandert wat een aanroep doet. label({"version": 3}) aanroepen draait de body niet. Het geeft een coroutine-object terug: de aanroep, klaargezet en gepauzeerd vóór de eerste regel, wachtend op iets wat hem draait. Er is nog niets wat "version 3" heeft berekend. Een coroutine-object dat je besluit niet te draaien, kun je weggooien met zijn close().
Wat drukt dit af?
async def label(version):
return f"version {version}"
pending = label(3)
print(type(pending).__name__)
pending.close()
Het type heet coroutine. Er verschijnt geen "version 3", omdat de body nooit draaide.
Het keyword await
Binnen een andere async def draai je een coroutine met await:
async def shout(snapshot):
line = await label(snapshot)
return line.upper()
await label(snapshot) draait de coroutine van label tot het eind en geeft de shout kan pauzeren. Als datgene waarop gewacht wordt moet wachten, zoals een send naar een trage kijker of de volgende snapshot, stopt de coroutine bij de await, draait de label hoeft nooit te wachten, dus hier keert de await gewoon terug. Elke await is een plek waar de controle terug kan naar de lus, en dat is de regel uit les 1, in de code geschreven.
await hoort alleen binnen async def. Overal anders weigert Python het voordat er ook maar één regel draait:
def shout(snapshot):
line = await label(snapshot)
return line.upper()
De melding is SyntaxError: 'await' outside async function, en op het hoogste niveau van een 'await' outside function. Een gewone def kan dus niet awaiten. Een functie die awaitet, moet zelf een coroutinefunctie zijn, en dat geldt ook voor wat haar awaitet, tot aan de ene buitenste coroutine die iets van buitenaf start.
Waar mag je await label(snapshot) schrijven?
asyncio.run start de lus
Iets moet de buitenste coroutine draaien. In de standaardbibliotheek is dat asyncio.run:
import asyncio
print(asyncio.run(shout({"version": 3})))
Het maakt een event loop, draait de coroutine die je het geeft tot het eind, samen met alles wat die coroutine awaitet, geeft de teruggegeven waarde van de coroutine terug, en sluit de lus. Een programma roept het meestal één keer aan, bovenaan, en al het andere wordt van daaruit ge-
asyncio.run aanroepen terwijl er al een lus draait, bijvoorbeeld vanuit een coroutine die asyncio.run startte, werpt RuntimeError: asyncio.run() cannot be called from a running event loop op. Binnen een coroutine is await other() de manier om een andere te draaien.
Die fout is de reden dat geen enkel voorbeeld op deze pagina asyncio.run aanroept. De voorbeelden die je op een pagina van deze site kunt uitvoeren, gebruiken een versie van Python die voor de browser is gebouwd, en die draait naar verluidt binnen de eigen event loop van de pagina, waar asyncio.run precies die fout zou opwerpen. Deze cursus heeft dat niet geverifieerd, dus hij rekent op geen van beide antwoorden: elke asyncio.run in dit hoofdstuk draait op de server, in de Run van deze les en de volgende.
Een coroutine die al draait onder asyncio.run roept asyncio.run(other()) aan. Wat gebeurt er?
await vergeten
Een coroutine die nooit wordt ge-await, draait nooit. Python merkt het als zo’n coroutine-object wordt weggegooid en drukt een waarschuwing af die haar noemt:
RuntimeWarning: coroutine 'label' was never awaited
Het programma gaat verder zonder het resultaat, dus een ontbrekende await laat zich zien als een
Deze les draait één coroutine tegelijk, die elk om de beurt worden ge-await. asyncio kan ook meerdere coroutines naast elkaar draaien als
Binnen two_labels schrijf je label(first) zonder await. Wat krijgt two_labels?
Probeer het
Dit deel is optioneel, en niets controleert het. Open async_scoreboard.py, het bestand waar het werk van dit hoofdstuk in komt. Schrijf label zoals hierboven, en schrijf two_labels(first, second): begin met een lege await label(first) toe, voeg await label(second) toe, en geef de lijst terug.
Klik op Run. Dat draait async_demo.py, dat je coroutines aanroept met asyncio.run op de server. De sectie van les 2 roept eerst label(...) aan zonder het te draaien en sluit de coroutine die het kreeg, en draait daarna allebei je coroutines. Een afgemaakte versie, op de site uitgevoerd, drukte af:
--- Lesson 2: a coroutine runs only when something runs it
calling label(...) alone gives a coroutine and runs nothing yet
asyncio.run(label(...)) gives 'version 3'
asyncio.run(two_labels(...)) gives ['version 4', 'version 5']
--- Lesson 3: your handler with a stand-in connection
not written yet
--- Lesson 3: your handler served by websockets.asyncio
pl_ws.py, a program written for this course, serves your handler with websockets.asyncio and plays three viewers:
async_scoreboard.viewer_handler is not written yet (Lesson 3)
De Run heeft de bibliotheek websockets nodig, omdat de meegeleverde async_scoreboard.py en pl_ws.py die bovenaan importeren; de sandbox van de site heeft haar. De eerste regel komt voordat er iets van jou draait, dus die verschijnt ook als je dit deel overslaat; totdat je label schrijft, is de volgende regel not written yet. De secties van les 3 wachten op de handler die je hierna schrijft.
In de volgende les wordt de kijkershandler van het scorebord een coroutine, en wordt het wachten dat hij doet een await.