← Назад к вопросам

Расскажи про эволюцию асинхронности

2.0 Middle🔥 71 комментариев
#DevOps и инфраструктура#Django

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Эволюция асинхронности в программировании

Асинхронность — это парадигма программирования, позволяющая выполнять долгие операции без блокировки основного потока выполнения. История её развития показывает эволюцию от простых callback'ов к современным async/await.

Этап 1: Синхронность (классический подход)

# Синхронный код — блокирует выполнение
import time

def fetch_data():
    """Долгая операция (I/O)"""
    time.sleep(2)  # Имитация сетевого запроса
    return "Data from server"

print("Начало")
data = fetch_data()  # Блокирует на 2 секунды
print(f"Данные: {data}")
print("Конец")

# Время выполнения: 2+ секунды
# Программа полностью заморожена во время wait_response

Проблема: при многих операциях программа становится очень медленной.

Этап 2: Callbacks (обратные вызовы)

JavaScript (Node.js)

// Callback-стиль (1990-2000е)
function fetchData(callback) {
    // Имитация асинхронной операции
    setTimeout(() => {
        callback('Data from server');
    }, 2000);
}

fetchData((data) => {
    console.log('Данные:', data);
});

console.log('Это выведется первым');

// Вывод:
// Это выведется первым
// [2 сек ожидания]
// Данные: Data from server

Проблема: Callback Hell (ад обратных вызовов)

// Вложенные callbacks становятся нечитаемыми
fetchUser((user) => {
    fetchPosts(user.id, (posts) => {
        fetchComments(posts[0].id, (comments) => {
            fetchReplies(comments[0].id, (replies) => {
                console.log(replies);
                // Кошмар!
            });
        });
    });
});

Этап 3: Promises (обещания)

JavaScript (ES6, 2015)

// Promise — объект, представляющий будущее значение
function fetchData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('Data from server');
        }, 2000);
    });
}

// Цепочка .then() вместо вложенных callbacks
fetchData()
    .then(data => {
        console.log('Данные:', data);
        return fetchPosts(data);
    })
    .then(posts => {
        console.log('Посты:', posts);
        return fetchComments(posts[0].id);
    })
    .then(comments => {
        console.log('Комментарии:', comments);
    })
    .catch(error => {
        console.error('Ошибка:', error);
    });

Преимущества Promise:

  • Линейное читаемое выполнение
  • Встроенная обработка ошибок через .catch()
  • Гибкость: Promise может использоваться многими способами
// Параллельное выполнение несколько Promises
Promise.all([
    fetchUser(),
    fetchPosts(),
    fetchComments()
])
.then(([user, posts, comments]) => {
    console.log('Всё загружено');
});

// Ожидание первого успешного
Promise.race([
    fetchFromServer1(),
    fetchFromServer2(),
    fetchFromServer3()
])
.then(firstResult => {
    console.log('Первый результат:', firstResult);
});

Этап 4: Async/Await (синтаксический сахар поверх Promise)

JavaScript (ES2017) и Python (3.5+)

// async/await выглядит как синхронный код, но работает асинхронно
async function getFullData() {
    try {
        const user = await fetchUser();  // Ждём, как в синхронном коде
        console.log('Пользователь:', user);
        
        const posts = await fetchPosts(user.id);
        console.log('Посты:', posts);
        
        const comments = await fetchComments(posts[0].id);
        console.log('Комментарии:', comments);
        
        return { user, posts, comments };
    } catch (error) {
        console.error('Ошибка:', error);
    }
}

getFullData();

Python версия

# Python (3.5+)
import asyncio
import aiohttp

async def fetch_data(url):
    """Асинхронный fetch"""
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.json()

async def main():
    user = await fetch_data('https://api.example.com/user')
    print('Пользователь:', user)
    
    posts = await fetch_data(f'https://api.example.com/posts/{user["id"]}')
    print('Посты:', posts)

# Запуск асинхронного кода
asyncio.run(main())

Преимущества async/await:

  • Выглядит как обычный синхронный код
  • Легче понять логику выполнения
  • Обработка ошибок через try/except

Этап 5: Event Loops (цикл событий)

Как асинхронность работает на самом деле

# Python asyncio использует Event Loop
import asyncio

async def task1():
    print("Task 1 начало")
    await asyncio.sleep(2)
    print("Task 1 конец")

async def task2():
    print("Task 2 начало")
    await asyncio.sleep(1)
    print("Task 2 конец")

async def task3():
    print("Task 3 начало")
    await asyncio.sleep(0.5)
    print("Task 3 конец")

async def main():
    # Запуск всех задач параллельно
    await asyncio.gather(
        task1(),
        task2(),
        task3()
    )

# Вывод:
# Task 1 начало
# Task 2 начало
# Task 3 начало
# Task 3 конец (после 0.5 сек)
# Task 2 конец (после 1 сек)
# Task 1 конец (после 2 сек)
# Время выполнения: 2 сек (параллельно!), не 3.5 сек

asyncio.run(main())

Event Loop схема:

┌─────────────────────────────────────┐
│  Event Loop (цикл событий)          │
├─────────────────────────────────────┤
│ 1. Выполнить синхронный код         │
│ 2. Если встречен await — подвесить  │
│ 3. Перейти к следующей корутине     │
│ 4. Когда операция завершена — вернуть│
│ 5. Повторить (1-4)                  │
└─────────────────────────────────────┘

Этап 6: Генераторы (Generators)

Python генераторы как предшественники async/await

# Генератор — функция, которая может приостановиться
def simple_generator():
    print("Начало")
    yield 1  # Приостановка
    print("Возобновление")
    yield 2  # Приостановка
    print("Конец")

gen = simple_generator()
print(next(gen))    # Начало, затем 1
print(next(gen))    # Возобновление, затем 2
# print(next(gen))  # StopIteration

# Для асинхронности (до async/await)
from collections.abc import Coroutine

def fetch_with_yield():
    print("Загружаю данные")
    # yield используется для обозначения асинхронной операции
    result = yield "loading"  # Приостановка
    print(f"Получил: {result}")
    return result

# Но это сложно для использования, поэтому пришёл async/await

Этап 7: Реактивное программирование (RxJS, ReactiveX)

JavaScript (RxJS)

// Реактивное программирование — потоки данных
import { interval, from } from 'rxjs';
import { map, filter, debounce } from 'rxjs/operators';

// Поток чисел каждые 1000мс
interval(1000)
    .pipe(
        map(x => x * 2),     // Умножить на 2
        filter(x => x > 5),  // Только больше 5
    )
    .subscribe(x => console.log(x));

// Поток событий клика
from(document.querySelectorAll('button'))
    .pipe(
        debounce(500)  // Дебаунс каждые 500мс
    )
    .subscribe(button => console.log('Нажата кнопка'));

Этап 8: Современные подходы (Parallel Processing)

Web Workers (параллелизм в браузере)

// main.js
const worker = new Worker('worker.js');

// Отправить тяжёлое вычисление в отдельный поток
worker.postMessage({ number: 1000000 });

worker.onmessage = (e) => {
    console.log('Результат:', e.data);
    // Основной поток не был заблокирован!
};
// worker.js
self.onmessage = (e) => {
    let sum = 0;
    for (let i = 0; i < e.data.number; i++) {
        sum += i;
    }
    self.postMessage(sum);
};

Сравнение подходов

ПодходГодЯзыкУдобствоПроизводительностьЧитаемость
Синхронность-ВсеПростаяПлохая✅ Отличная
Callbacks2000JS, Python❌ Адский адХорошая❌ Плохая
Promises2015JS👍 ХорошоХорошая👍 Хорошая
Async/Await2017JS, Python✅ ОтличнаяОтличная✅ Отличная
Generators2015Python👍 СредняяОтличная👍 Хорошая
RxJS2010JS👍 СредняяОтличная🤔 Сложная
Web Workers2010JS👍 Хорошо✅ Отличная👍 Хорошая

Практический пример: миграция с кода

// Исходный синхронный код
function loadUserData() {
    const user = fetch('/api/user');  // Блокирует!
    const posts = fetch(`/api/posts/${user.id}`);  // Блокирует!
    return { user, posts };
}

// Callback версия
function loadUserData(callback) {
    fetch('/api/user', (user) => {
        fetch(`/api/posts/${user.id}`, (posts) => {
            callback({ user, posts });
        });
    });
}

// Promise версия
function loadUserData() {
    return fetch('/api/user')
        .then(user => fetch(`/api/posts/${user.id}`)
            .then(posts => ({ user, posts }))
        );
}

// Async/Await версия (СОВРЕМЕННЫЙ СТАНДАРТ)
async function loadUserData() {
    const user = await fetch('/api/user');
    const posts = await fetch(`/api/posts/${user.id}`);
    return { user, posts };
}

Производительность: параллельное выполнение

# Синхронный код: 3 запроса по 1 сек = 3 сек
for url in ['api/1', 'api/2', 'api/3']:
    result = requests.get(url)  # Ждём 1 сек
# Итого: 3 сек

# Асинхронный код: 3 запроса по 1 сек = 1 сек (параллельно)
import asyncio
import aiohttp

async def main():
    async with aiohttp.ClientSession() as session:
        tasks = [
            session.get(url)
            for url in ['api/1', 'api/2', 'api/3']
        ]
        results = await asyncio.gather(*tasks)
# Итого: 1 сек (параллельное выполнение)

asyncio.run(main())

Вывод

  1. Callbacks (2000) → Promises (2015) → Async/Await (2017) — путь совершенствования
  2. Async/Await — де-факто стандарт в современном программировании
  3. Event Loop — ключ к пониманию того, как работает асинхронность
  4. Параллелизм (Web Workers, ThreadPools) — для CPU-bound операций
  5. Асинхронность — для I/O-bound операций (сеть, файлы, БД)
Расскажи про эволюцию асинхронности | PrepBro