← Назад к вопросам
Нужно ли делать отдельный catch для каждого then в Promise?
2.3 Middle🔥 271 комментариев
#JavaScript Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Нужно ли делать отдельный catch для каждого then?
Нет, обычно не нужно. Один catch в конце цепи Promise обработает ошибки со всех предыдущих .then(). Это одно из самых полезных свойств Promise цепочек.
Как работает обработка ошибок в Promise цепи
Один catch на конце (правильно)
fetch('/api/users/1')
.then(response => response.json())
.then(user => {
console.log('User loaded:', user.name);
return user;
})
.then(user => updateUI(user))
.catch(error => {
// Обработает ошибку с ЛЮБОГО из предыдущих .then()
console.error('Error:', error.message);
});
Что перехватит этот catch:
- Ошибка в fetch (сеть недоступна)
- Ошибка парсинга JSON
- Ошибка в логировании (если user.name undefined)
- Ошибка в updateUI
Catch обрабатывает как Promise reject, так и обычные ошибки
Promise.resolve()
.then(() => {
// Обычная ошибка в .then
throw new Error('Что-то пошло не так!');
})
.then(() => {
console.log('Это не выполнится');
})
.catch(error => {
// Перехватит Error выше
console.error('Caught:', error.message); // "Что-то пошло не так!"
});
Когда нужны отдельные catch?
1. Разные действия для разных ошибок
fetch('/api/users/1')
.then(response => {
if (response.status === 404) {
throw new NotFoundError('User not found');
}
return response.json();
})
.catch(error => {
// Обработаем NOT FOUND специально
if (error instanceof NotFoundError) {
redirectTo('/users');
return null; // Можно вернуть default значение
}
throw error; // Пробросить дальше
})
.then(user => {
if (user !== null) {
updateUI(user);
}
})
.catch(error => {
// Общая обработка других ошибок
showError('Failed to load user');
});
2. Восстановление после ошибки
fetch('/api/users/1')
.then(response => response.json())
.catch(error => {
// Пытаемся загрузить из кэша
console.log('API failed, using cache');
return getFromCache('users/1');
})
.then(user => {
updateUI(user);
})
.catch(error => {
// Если и кэш не помог
showError('No data available');
});
3. Промежуточная обработка
async function getUserAndPosts(userId) {
return fetch(`/api/users/${userId}`)
.then(r => r.json())
.catch(error => {
// Если не удалось загрузить юзера
logger.error('User loading failed:', error);
throw error; // Пробросить выше
})
.then(user => {
// Теперь загружаем посты
return fetch(`/api/users/${user.id}/posts`)
.then(r => r.json())
.catch(error => {
// Если не удалось загрузить посты
logger.error('Posts loading failed:', error);
return []; // Вернуть пустой массив
})
.then(posts => ({ user, posts }));
});
}
Практический пример: API запрос
Плохо: catch для каждого then
fetch('/api/data')
.then(r => r.json())
.catch(error => console.error('Parse error:', error)) // Избыточно
.then(data => processData(data))
.catch(error => console.error('Process error:', error)) // Избыточно
.then(result => displayResult(result))
.catch(error => console.error('Display error:', error)); // Избыточно
Хорошо: один catch на конце
fetch('/api/data')
.then(r => r.json())
.then(data => processData(data))
.then(result => displayResult(result))
.catch(error => {
// Единая обработка ВСЕХ ошибок
console.error('Operation failed:', error);
showErrorNotification(error);
});
Лучшее: с промежуточной обработкой
fetch('/api/data')
.then(r => r.json())
.catch(error => {
// Специальная обработка JSON ошибок
if (error instanceof SyntaxError) {
console.error('Invalid JSON response');
}
throw error;
})
.then(data => processData(data))
.then(result => displayResult(result))
.catch(error => {
// Финальная обработка
showErrorNotification('Failed to load data');
});
Сравнение с async/await (рекомендуется)
Основная проблема Promise цепочек - сложность. async/await чище:
// Promise цепь
fetch('/api/data')
.then(r => r.json())
.then(data => processData(data))
.then(result => displayResult(result))
.catch(error => {
console.error('Error:', error);
});
// async/await (чище!)
async function loadData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
const result = await processData(data);
displayResult(result);
} catch (error) {
console.error('Error:', error);
}
}
Распространенные ошибки
Ошибка 1: Забыть пробросить ошибку
// Неправильно: catch молча игнорирует ошибку
fetch('/api/data')
.then(r => r.json())
.catch(error => {
console.log('Error occurred'); // Логируем, но не пробрасываем
})
.then(data => {
// data будет undefined!
processData(data); // Будет ошибка
});
// Правильно: пробросить или вернуть значение
.catch(error => {
console.log('Error occurred');
throw error; // Или вернуть default: return null;
})
Ошибка 2: Забыть return в .then
// Неправильно: цепь разрывается
fetch('/api/users')
.then(r => r.json())
.then(users => {
users.forEach(user => console.log(user.name));
// Не вернули значение!
})
.then(result => {
console.log(result); // undefined
});
// Правильно
.then(users => {
console.log(users.length);
return users; // Вернуть для следующего .then
})
Ошибка 3: Забыть return в .catch
// Неправильно
fetch('/api/data')
.catch(error => {
logger.error(error);
// Не пробросили, цепь продолжит с undefined
})
.then(data => {
console.log(data); // undefined!
});
// Правильно
.catch(error => {
logger.error(error);
return { default: 'data' }; // Вернуть default или пробросить
// throw error; // Альтернатива
})
Правила обработки ошибок
1. Один catch в конце для разных ошибок с одной обработкой
fetch('/api/data')
.then(r => r.json())
.then(processData)
.then(displayUI)
.catch(error => showErrorModal(error));
2. Отдельные catch для специальной обработки
fetch('/api/data')
.then(r => r.json())
.catch(parseError => {
// Специальная обработка ошибок парсинга
logger.error('JSON parse error');
throw parseError;
})
.then(processData)
.catch(processError => {
// Специальная обработка ошибок обработки
logger.error('Processing failed');
throw processError;
})
.then(displayUI)
.catch(displayError => {
// Финальная обработка
showErrorModal(displayError);
});
3. Лучшее решение: async/await
async function loadAndDisplay() {
try {
const response = await fetch('/api/data');
const data = await response.json();
const processed = await processData(data);
displayUI(processed);
} catch (error) {
if (error instanceof SyntaxError) {
logger.error('JSON parse error');
} else if (error instanceof ProcessError) {
logger.error('Processing failed');
} else {
logger.error('Unexpected error');
}
showErrorModal(error);
}
}
Checklist: Обработка ошибок Promise
- Не нужен catch после каждого then
- Один catch в конце перехватит все ошибки
- Используй отдельный catch только для специальной обработки
- Всегда пробрасывай (throw) или возвращай значение из catch
- Предпочитай async/await вместо цепочек Promise
- Логируй ошибки для отладки
Вывод
Нет, не нужен отдельный catch для каждого then. Один catch в конце обработает ошибки со всех предыдущих .then(). Используй отдельные catch только если нужна специальная обработка определенной ошибки. Для новых проектов переходи на async/await - это чище и понятнее.