Асинхронное программирование в TypeScript: полное руководство

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение в асинхронное программирование в TypeScript

Современная веб-разработка немыслима без асинхронного программирования. Будь то запросы к API, работа с файловой системой или таймеры — все эти операции требуют неблокирующего выполнения кода. TypeScript, как надстройка над JavaScript, предоставляет мощные инструменты для работы с асинхронностью, добавляя строгую типизацию и улучшенную читаемость кода.

В этом руководстве мы подробно разберем все аспекты асинхронного программирования в TypeScript: от базовых Promise до продвинутых паттернов с async/await и параллельного выполнения задач.

1. Основы Promise в TypeScript

Что такое Promise?

Promise (обещание) — это объект, представляющий результат асинхронной операции. Он может находиться в одном из трех состояний:

  • Pending (ожидание) — начальное состояние, операция еще не завершена.
  • Fulfilled (выполнено) — операция завершена успешно.
  • Rejected (отклонено) — операция завершилась с ошибкой.

Создание Promise с типизацией

TypeScript позволяет строго типизировать возвращаемые значения Promise. Рассмотрим пример:

// Функция, возвращающая Promise с типом данных
function fetchUserData(userId: number): Promise<User> {
    return new Promise<User>((resolve, reject) => {
        setTimeout(() => {
            const user: User = {
                id: userId,
                name: 'Иван Иванов',
                email: 'ivan@example.com'
            };
            
            if (userId > 0) {
                resolve(user);
            } else {
                reject(new Error('Некорректный ID пользователя'));
            }
        }, 1000);
    });
}

// Интерфейс для типизации данных
interface User {
    id: number;
    name: string;
    email: string;
}

// Использование Promise
fetchUserData(1)
    .then((user: User) => {
        console.log(`Пользователь: ${user.name}`);
    })
    .catch((error: Error) => {
        console.error(`Ошибка: ${error.message}`);
    });

Цепочки Promise (Chaining)

Promise можно объединять в цепочки для последовательного выполнения асинхронных операций:

function getUserPosts(userId: number): Promise<string[]> {
    return new Promise((resolve) => {
        setTimeout(() => resolve(['Пост 1', 'Пост 2', 'Пост 3']), 500);
    });
}

function getPostComments(postTitle: string): Promise<string[]> {
    return new Promise((resolve) => {
        setTimeout(() => resolve(['Комментарий 1', 'Комментарий 2']), 300);
    });
}

// Цепочка Promise
fetchUserData(1)
    .then((user) => {
        console.log(`Пользователь найден: ${user.name}`);
        return getUserPosts(user.id);
    })
    .then((posts) => {
        console.log(`Постов найдено: ${posts.length}`);
        return getPostComments(posts[0]);
    })
    .then((comments) => {
        console.log(`Комментариев к первому посту: ${comments.length}`);
    })
    .catch((error) => {
        console.error('Произошла ошибка:', error);
    });

2. Async/Await — современный подход

Синтаксис async/await

Ключевые слова async и await делают асинхронный код похожим на синхронный, что значительно улучшает его читаемость. Функция, помеченная async, всегда возвращает Promise.

// Асинхронная функция с типизацией возвращаемого значения
async function loadUserProfile(userId: number): Promise<UserProfile> {
    try {
        const user = await fetchUserData(userId);
        const posts = await getUserPosts(userId);
        
        return {
            user,
            posts,
            lastLogin: new Date()
        };
    } catch (error) {
        console.error('Не удалось загрузить профиль:', error);
        throw error; // Пробрасываем ошибку дальше
    }
}

interface UserProfile {
    user: User;
    posts: string[];
    lastLogin: Date;
}

// Использование async/await
async function displayProfile() {
    try {
        const profile = await loadUserProfile(1);
        console.log(`Профиль пользователя ${profile.user.name}`);
        console.log(`Постов: ${profile.posts.length}`);
        console.log(`Последний вход: ${profile.lastLogin}`);
    } catch (error) {
        console.error('Ошибка отображения профиля:', error);
    }
}

displayProfile();

Обработка ошибок в async/await

Для обработки ошибок в асинхронных функциях используется конструкция try/catch. TypeScript позволяет типизировать ошибки через пользовательские классы:

class ApiError extends Error {
    constructor(
        public statusCode: number,
        message: string
    ) {
        super(message);
        this.name = 'ApiError';
    }
}

class NetworkError extends Error {
    constructor(message: string) {
        super(message);
        this.name = 'NetworkError';
    }
}

async function fetchWithRetry(url: string, retries: number = 3): Promise<Response> {
    for (let attempt = 1; attempt <= retries; attempt++) {
        try {
            const response = await fetch(url);
            
            if (!response.ok) {
                throw new ApiError(
                    response.status,
                    `HTTP ошибка: ${response.status}`
                );
            }
            
            return response;
        } catch (error) {
            if (error instanceof ApiError) {
                throw error; // Не повторяем при HTTP ошибках
            }
            
            if (attempt === retries) {
                throw new NetworkError(
                    `Не удалось выполнить запрос после ${retries} попыток`
                );
            }
            
            console.log(`Попытка ${attempt} не удалась, повторяем...`);
            await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
        }
    }
    
    throw new Error('Недостижимый код');
}

3. Параллельное выполнение асинхронных операций

Promise.all — ожидание всех Promise

Метод Promise.all позволяет запустить несколько асинхронных операций параллельно и дождаться завершения всех:

interface DashboardData {
    user: User;
    posts: string[];
    notifications: number;
}

async function loadDashboard(userId: number): Promise<DashboardData> {
    try {
        // Параллельное выполнение независимых запросов
        const [user, posts, notifications] = await Promise.all([
            fetchUserData(userId),
            getUserPosts(userId),
            getNotificationCount(userId)
        ]);
        
        return {
            user,
            posts,
            notifications
        };
    } catch (error) {                

Блоги

Рекомендаций по книгам