Ein Vorgeschmack auf Async
Was dieser Vorgeschmack auslässt
Dieses Kapitel hat einen Vorgeschmack versprochen, und hier endet er. Du kannst jetzt einen async def-Handler lesen und jede Stelle finden, an der er die Kontrolle zurückgibt: jedes await. Du hast eine asyncio.run auf dem Server ausgeführt und den Zuschauer-Handler der Anzeigetafel als Coroutine geschrieben, die next_snapshot(), send und close abwartet. Und du hast die Form eines websockets.asyncio-Servers gelesen. Diese Lektion nennt, was ein echter Kurs über asyncio hinzufügen würde, damit du weißt, wo dein Wissen aufhört, und liest dann die Version eines weiteren Frameworks des Handlers, den du geschrieben hast.
Tasks und Abbruch
Im Run von Lektion 3 wurden drei Zuschauer gleichzeitig in einem Thread bedient. Der asyncio-Server hat das geschafft, indem er den Handler jeder Verbindung als await wechselt. Du hast nie selbst einen Task gestartet. next_snapshot() kam fertig mit, und der Server hat für jeden Handler einen Task gestartet.
Echte asynchrone Programme starten ständig eigene Tasks: ein Handler, der auf den nächsten Snapshot wartet und gleichzeitig seine Verbindung beobachtet, ein Server, der einen Punktezähler und eine Uhr nebeneinander ausführt, ein Client, der gleichzeitig mit drei Servern spricht. Einen Task starten, darauf warten, dass mehrere fertig werden (eine asyncio.TaskGroup ist das Standardwerkzeug), und sie sauber stoppen, indem man einen Task abbricht und ihn aufräumen lässt, wenn man ihm sagt, dass er aufhören soll: Das sind Fähigkeiten mit eigenen Regeln. Sie werden hier genannt und nicht gelehrt. Sie sind das Herzstück des Kurses Async Python, der nur Python in der Praxis voraussetzt.
Welches davon ist ein Thema für den Kurs Async Python und keins, das dieser Kurs gelehrt hat?
Blockierende Aufrufe in einer Loop
Lektion 3 hat time.sleep und Event.wait aus dem Handler herausgehalten, weil ein blockierender Aufruf den einzigen Thread der Loop festhält. Diese beiden sind leicht zu erkennen. Andere verstecken sich besser: ein recv auf einem einfachen await auf ein Netzwerk oder eine Festplatte wartet. In einer Coroutine hält jeder davon alle anderen Zuschauer an, bis er zurückkehrt.
Es gibt zwei übliche Heilmittel. Verwende eine Bibliothek, die für die Loop gebaut ist, so wie websockets.asyncio den synchronen Server ersetzt, den du vorher verwendet hast. Oder gib den blockierenden Aufruf an einen Thread ab, damit die Loop weitermachen kann, während er wartet; asyncio hat dafür eine asyncio.to_thread. Zu wissen, welche Aufrufe blockieren, und zwischen diesen Heilmitteln zu wählen, ist wieder Thema von Async Python.
Welche Zeile in einem async def-Handler hält jeden anderen Zuschauer an, während sie läuft?
Asynchronen Code testen
Als du in Lektion 3 auf Submit geklickt hast, hat die Prüfung deine Coroutine von Hand Schritt für Schritt ausgeführt, mit Stellvertretern, die an markierten Stellen die Kontrolle zurückgaben, und ganz ohne async def-Testfunktionen ausführt, oder mit asyncio.run in einem gewöhnlichen Test, und sie testen auch Timeouts und
Noch einmal FastAPI
In @app.get(...) gelesen. FastAPI hat auch
from fastapi import FastAPI, WebSocket
from fastapi.testclient import TestClient
app = FastAPI()
@app.websocket("/watch")
async def watch(websocket: WebSocket):
await websocket.accept()
await websocket.send_json({"type": "snapshot", "version": 0, "scores": {"Owls": 0}})
await websocket.close(code=1000, reason="the quiz is over")
with TestClient(app).websocket_connect("/watch") as ws:
print(ws.receive_json())
ws.receive_json()
Lies ihn neben deinem Handler. @app.websocket("/watch") registriert die Coroutine für WebSocket-/watch, so wie @app.get eine Funktion für GET-Anfragen registriert hat. Die Annotation in websocket: WebSocket, ebenfalls nur zum Lesen, ist das, woran FastAPI erkennt, dass es die Verbindung übergeben soll. await websocket.accept() ist der Aufruf, der den Server das Upgrade mit 101 Switching Protocols beantworten lässt; bis dahin könnte die Anfrage noch abgelehnt werden, so wie dein Code aus Kapitel 2 sie abgelehnt hat. send_json codiert das close sendet den Close-TestClient, den du kennst: websocket_connect führt den Handshake im selben Prozess durch, ohne
Die Beispiele dieser Lektion laufen in deinem Browser, wo FastAPI nicht verfügbar ist, also läuft dieser Code hier nicht. Die Server-Sandbox der Seite hat FastAPI und seinen Test-Client, und dort hat der Test den Snapshot empfangen, {'type': 'snapshot', 'version': 0, 'scores': {'Owls': 0}}, und das zweite receive_json() hat WebSocketDisconnect mit dem Code 1000 und dem Grund the quiz is over ausgelöst.
Welchem Teil deines Codes aus Kapitel 2 entspricht await websocket.accept() im FastAPI-Endpunkt?
Das ist der Rand des Vorgeschmacks. Das nächste Kapitel macht aus diesen Rändern Wege, zwischen denen du wählen kannst.