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

Как использовать try catch в Async/await?

1.0 Junior🔥 122 комментариев
#JavaScript Core

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

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

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

Try/Catch в Async/Await

Это фундаментальный паттерн обработки ошибок в асинхронном коде. Давай разберемся, как правильно ловить ошибки.

Базовый синтаксис

async function fetchUser(id) {
  try {
    const response = await fetch(`/api/users/${id}`);
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }
    const user = await response.json();
    return user;
  } catch (error) {
    console.error("Ошибка загрузки пользователя:", error.message);
    return null;
  }
}

Как это работает:

  1. try блок выполняется
  2. Если где-то выбросится ошибка (throw или отклоненный Promise) — прыгаем в catch
  3. В catch обрабатываем ошибку
  4. finally выполняется в любом случае (опционально)

Реальный пример: загрузка данных

async function loadUserProfile(userId) {
  try {
    // Загружаем пользователя
    const userRes = await fetch(`/api/users/${userId}`);
    if (!userRes.ok) throw new Error("Пользователь не найден");
    const user = await userRes.json();
    
    // Загружаем его посты
    const postsRes = await fetch(`/api/users/${userId}/posts`);
    if (!postsRes.ok) throw new Error("Посты не найдены");
    const posts = await postsRes.json();
    
    return { user, posts };
    
  } catch (error) {
    console.error("Ошибка:", error.message);
    throw error; // Пробросим выше
  }
}

Различные типы ошибок

В catch блоке может быть разная информация:

async function operation() {
  try {
    await fetch("/api/data");
  } catch (error) {
    // error может быть разного типа
    
    if (error instanceof TypeError) {
      // Ошибка сети или синтаксиса
      console.log("Сетевая ошибка");
    } else if (error instanceof SyntaxError) {
      // JSON невалидный
      console.log("Невалидный JSON");
    } else if (error.name === "AbortError") {
      // Запрос отменили
      console.log("Запрос отменен");
    } else {
      // Какая-то другая ошибка
      console.log("Неизвестная ошибка");
    }
  }
}

Finally блок

Это выполняется в любом случае — пригодится для очистки:

async function fetchWithTimeout(url, timeout = 5000) {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeout);
  
  try {
    const response = await fetch(url, { signal: controller.signal });
    return await response.json();
  } catch (error) {
    console.error("Ошибка:", error.message);
  } finally {
    // Очищаем таймер в любом случае
    clearTimeout(timeoutId);
  }
}

Обработка нескольких операций

Последовательно (одна за другой):

async function processUser(userId) {
  try {
    const user = await fetchUser(userId);
    const settings = await fetchSettings(userId);
    const notifications = await fetchNotifications(userId);
    
    return { user, settings, notifications };
    
  } catch (error) {
    console.error("Ошибка:", error);
    // Если любая операция упадет — все дальше не выполняются
  }
}

Параллельно (всё сразу):

async function processUser(userId) {
  try {
    // Promise.all бросит ошибку если хоть одна упадет
    const [user, settings, notifications] = await Promise.all([
      fetchUser(userId),
      fetchSettings(userId),
      fetchNotifications(userId)
    ]);
    
    return { user, settings, notifications };
    
  } catch (error) {
    console.error("Ошибка:", error);
  }
}

Параллельно, но с частичной обработкой ошибок:

async function processUser(userId) {
  const results = await Promise.allSettled([
    fetchUser(userId),
    fetchSettings(userId),
    fetchNotifications(userId)
  ]);
  
  // Каждый результат может быть fulfilled или rejected
  const [userResult, settingsResult, notificationsResult] = results;
  
  const data = {};
  if (userResult.status === "fulfilled") {
    data.user = userResult.value;
  } else {
    console.error("Ошибка загрузки пользователя:", userResult.reason);
  }
  
  // Аналогично для других
  return data;
}

Пример в React

function UserPage({ userId }) {
  const [user, setUser] = useState(null);
  const [error, setError] = useState(null);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    let isMounted = true; // Для предотвращения утечек памяти
    
    async function loadUser() {
      try {
        setLoading(true);
        setError(null);
        
        const response = await fetch(`/api/users/${userId}`);
        if (!response.ok) throw new Error(`HTTP ${response.status}`);
        
        const data = await response.json();
        
        if (isMounted) {
          setUser(data);
        }
      } catch (err) {
        if (isMounted) {
          setError(err.message);
        }
      } finally {
        if (isMounted) {
          setLoading(false);
        }
      }
    }
    
    loadUser();
    
    return () => {
      isMounted = false; // Cleanup
    };
  }, [userId]);
  
  if (loading) return <div>Загружается...</div>;
  if (error) return <div>Ошибка: {error}</div>;
  return <div>{user.name}</div>;
}

Кастомная обработка ошибок

class ApiError extends Error {
  constructor(statusCode, message) {
    super(message);
    this.statusCode = statusCode;
  }
}

async function apiCall(url, options = {}) {
  try {
    const response = await fetch(url, options);
    
    if (!response.ok) {
      throw new ApiError(
        response.status,
        await response.text()
      );
    }
    
    return await response.json();
    
  } catch (error) {
    if (error instanceof ApiError) {
      // Обрабатываем HTTP ошибки
      if (error.statusCode === 401) {
        // Перенаправить на логин
      } else if (error.statusCode === 403) {
        // Показать "Доступ запрещен"
      }
    } else if (error instanceof TypeError) {
      // Сетевая ошибка
      console.error("Нет интернета");
    }
    
    throw error; // Пробросим выше
  }
}

Retry логика

async function fetchWithRetry(url, maxAttempts = 3) {
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      const response = await fetch(url);
      if (!response.ok) throw new Error(`HTTP ${response.status}`);
      return await response.json();
      
    } catch (error) {
      if (attempt === maxAttempts) {
        throw error; // Последняя попытка, бросим ошибку
      }
      
      // Ждем перед следующей попыткой
      const delay = Math.pow(2, attempt - 1) * 1000; // 1s, 2s, 4s...
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}

Лучшие практики

  1. Всегда обрабатывай ошибки, не оставляй Promise отклоненными
  2. Проверяй HTTP статусы — fetch не бросает ошибку при 404 или 500
  3. Используй finally для очистки — таймауты, соединения, подписки
  4. Пробрасывай ошибки выше если необходимо — не поглощай их просто так
  5. В React — проверяй isMounted перед setState в asyn функциях
  6. Логируй детали ошибок для отладки, но не содержимое в production
  7. Используй Promise.allSettled для параллельных операций где нужна частичная обработка

Try/catch с async/await — это самый читаемый способ обработки ошибок в JavaScript.