Как писали асинхронный код до появления async/await?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Асинхронное программирование до async/await: от колбэков к промисам и генераторам
До появления async/await (Python 3.5, 2015) асинхронное программирование было намного сложнее. Разработчики использовали различные подходы: колбэки, промисы, генераторы и различные фреймворки.
1. Колбэки (Callbacks)
Первый способ асинхронного программирования — использование функций обратного вызова.
import threading
def fetch_user(user_id, callback):
def _fetch():
import time
time.sleep(1)
user = {"id": user_id, "name": "Alice"}
callback(user)
thread = threading.Thread(target=_fetch)
thread.start()
def on_user_fetched(user):
print(f"User: {user}")
fetch_user(1, on_user_fetched)
Проблемы: "Callback Hell" — вложенность становится нечитаемой.
2. Generators + yield
Генераторы позволяли приостанавливать и возобновлять выполнение.
def fetch_user_generator():
print("Fetching user...")
user = yield "fetch_user" # Приостанавливаем
print(f"User: {user}")
return user
gen = fetch_user_generator()
request = next(gen)
result = gen.send({"id": 1, "name": "Alice"})
3. Twisted Framework
Один из первых асинхронных фреймворков на Python.
from twisted.internet import defer, reactor
@defer.inlineCallbacks
def fetch_data():
try:
response = yield make_request("http://example.com/api")
print(f"Response: {response}")
except Exception as e:
print(f"Error: {e}")
finally:
reactor.stop()
reactor.callWhenRunning(fetch_data)
reactor.run()
4. Tornado Web Framework
Популярный фреймворк для асинхронной веб-разработки.
import tornado.ioloop
import tornado.httpclient
class Handler(tornado.web.RequestHandler):
@tornado.web.asynchronous
def get(self):
http_client = tornado.httpclient.AsyncHTTPClient()
def handle_response(response):
self.write(response.body)
self.finish()
http_client.fetch("http://example.com/api", callback=handle_response)
5. asyncio + yield from (Python 3.3-3.4)
Переходный синтаксис к modern async/await.
import asyncio
@asyncio.coroutine
def fetch_user(user_id):
yield from asyncio.sleep(1)
return {"id": user_id, "name": "Alice"}
@asyncio.coroutine
def main():
user = yield from fetch_user(1) # yield from вместо await
print(f"User: {user}")
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Обработка ошибок до async/await
Большая сложность в каждом колбэке.
def fetch_with_error_handling(callback, error_callback):
try:
result = perform_operation()
callback(result)
except Exception as e:
error_callback(e)
Callback Hell пример
# ❌ Ужасная вложенность
def callback_hell():
def step1(data1):
def step2(data2):
def step3(data3):
print(f"Result: {data1} + {data2} + {data3}")
fetch_data(step3)
fetch_data(step2)
fetch_data(step1)
# ✅ Современный async/await
async def modern():
data1 = await fetch_data()
data2 = await fetch_data()
data3 = await fetch_data()
print(f"Result: {data1} + {data2} + {data3}")
Параллельное выполнение
До async/await нужно было использовать asyncio.gather или инструменты фреймворков.
import asyncio
@asyncio.coroutine
def old_concurrent():
tasks = [
asyncio.ensure_future(fetch_data(1)),
asyncio.ensure_future(fetch_data(2)),
asyncio.ensure_future(fetch_data(3)),
]
results = yield from asyncio.gather(*tasks)
return results
Трудности
- Управление ошибками сложное и многословное
- Callback Hell — вложенность часто вызывала головную боль
- Отладка — сложнее понять стек вызовов
- Разные API — каждый фреймворк имел свой способ
- Читаемость — асинхронный код выглядел совсем не как синхронный
Преимущества async/await
Когда в Python 3.5 появился async/await:
- Синтаксис похож на синхронный код
- Легче читать и писать
- Встроенная обработка ошибок (try/except)
- Унифицированный API
- Намного легче отлаживать
Заключение
До async/await асинхронное программирование в Python требовало глубокого понимания event loop, колбэков и различных фреймворков. Разработчикам приходилось выбирать между Twisted, Tornado, asyncio и другими решениями, каждое с своим синтаксисом. Появление async/await революционизировало разработку, сделав асинхронный код таким же легким для понимания, как синхронный.