Programowanie asynchroniczne pojawiło się w języku Python od wersji 3.5 z wprowadzeniem słów kluczowych async i await. Początkowo do zadań asynchronicznych używano takich bibliotek jak asyncio oraz generatorów opartych na korutinach, co było trudne do zrozumienia i utrzymania. Po wprowadzeniu składni async/await kod asynchroniczny stał się bardziej oczywisty i czytelny, zbliżony do tradycyjnego stylu synchronicznego.
Przed pojawieniem się async/await asynchroniczność była realizowana za pomocą callbacków i generatorów (na przykład z użyciem biblioteki tornado lub starych interfejsów API asyncio). Taki kod był trudny do debugowania i utrzymania.
Głównym problemem przy przetwarzaniu dużej liczby równoczesnych operacji I/O (zapytania sieciowe, operacje na plikach) w kodzie synchronicznym jest blokada głównego wątku. Prowadzi to do spadku wydajności i niemożności efektywnego wykorzystania zasobów.
Programowanie asynchroniczne przy użyciu async/await pozwala na jednoczesne wykonywanie wielu operacji wejścia-wyjścia w ramach jednego wątku, unikając blokad. Przy tym składnia jest bliska zwykłym funkcjom, co ułatwia czytelność i debugowanie.
import asyncio async def fetch_data(delay): print(f"Start fetching after {delay}s delay") await asyncio.sleep(delay) print(f"Done fetching after {delay}s delay") return delay async def main(): results = await asyncio.gather( fetch_data(1), fetch_data(2), fetch_data(3) ) print("Results:", results) asyncio.run(main())
Czy funkcja zdefiniowana za pomocą async def może być wywołana jak zwykła funkcja?
Nie. Wywołanie takiej funkcji zwraca obiekt korutyny, który nie jest wykonywany do momentu przekazania go do pętli zdarzeń (na przykład za pomocą await lub asyncio.run()).
def foo(): return 42 async def bar(): return 42 print(foo()) # 42 print(bar()) # <coroutine object bar at ...>
Czy można użyć await poza asynchroniczną funkcją?
Nie. Słowo kluczowe await powinno być używane tylko wewnątrz funkcji zadeklarowanych za pomocą async def. Próba użycia await poza taką funkcją spowoduje błąd składni (SyntaxError).
# Błąd! await asyncio.sleep(1) # SyntaxError: 'await' outside async function
Czy asynchroniczność działa dla operacji nie związanych z I/O (na przykład obliczenia)?
Nie. Asynchroniczność jest skuteczna tylko dla operacji wejścia-wyjścia. Dla zadań obliczeniowych wciąż potrzebny jest multiprocessing lub threading, inaczej zostanie zablokowana pętla zdarzeń.
Zalety:
Wady:
Negatywny przypadek: Młodzi programiści postanowili przyspieszyć aplikację za pomocą async/await, ale asynchronicznie wykonywali tylko obliczenia, a nie zapytania sieciowe. Aplikacja się nie przyspieszyła. Zalety: zapoznali się z składnią. Wady: brak zysków, kod stał się bardziej skomplikowany.
Pozytywny przypadek: Asynchronicznie obsługiwali tysiące zapytań do API. Serwer zaczął obsługiwać więcej klientów bez zwiększania zasobów. Zalety: znacznie wzrosła wydajność, architektura stała się prostsza. Wady: wzrósł próg wejścia dla nowicjuszy.