catch() нужен, чтобы обработать ошибку промиса. Метод вызывается у объекта Promise, перехватывает отклонённый результат или ошибку из предыдущего шага цепочки и возвращает новый промис.
Синтаксис
Базовая запись выглядит так:
promise.catch(onRejected)
Где:
promise- это объектPromiseonRejected- функция, которая выполнится, если промис завершился с ошибкой
Обычно в onRejected передают объект ошибки или другое значение, с которым промис был отклонён.
Что возвращает catch()
catch() всегда возвращает новый промис.
Это важный момент, потому что после обработки ошибки цепочку можно продолжить:
const promise = Promise.reject(new Error("boom"));
const nextPromise = promise.catch((error) => error.message);
console.log(nextPromise instanceof Promise); // true
nextPromise.then((value) => {
console.log(value); // "boom"
});
Результат catch() - не обычное значение, а новый промис.
Простой пример
Promise.reject(new Error("Не удалось загрузить данные"))
.catch((error) => {
console.log(error.message); // "Не удалось загрузить данные"
});
Если промис переходит в состояние rejected, catch() получает причину ошибки.
catch() перехватывает отклонённый промис
Это базовый сценарий использования.
const promise = Promise.reject(new Error("Ошибка запроса"));
promise.catch((error) => {
console.log(error.message); // "Ошибка запроса"
});
Если промис завершился с ошибкой, обработчик из catch() выполнится.
catch() перехватывает ошибки из предыдущего then()
Это ещё одна очень важная особенность.
catch() ловит не только уже отклонённые промисы, но и ошибки, которые были выброшены внутри предыдущих обработчиков then().
Promise.resolve(5)
.then(() => {
throw new Error("Что-то пошло не так");
})
.catch((error) => {
console.log(error.message); // "Что-то пошло не так"
});
Поэтому catch() удобно ставить в конце цепочки, если нужно централизованно обработать ошибку.
Если в catch() вернуть значение, цепочка продолжится как успешная
Это один из самых полезных нюансов метода.
Если обработчик ошибки вернул обычное значение, следующий шаг получит его уже как успешный результат.
Promise.reject(new Error("boom"))
.catch((error) => {
console.log(error.message); // "boom"
return "Запасной результат";
})
.then((value) => {
console.log(value); // "Запасной результат"
});
Это поведение часто используют, когда нужно подставить значение по умолчанию и продолжить работу.
Если в catch() вернуть промис, цепочка подождёт его
Как и then(), метод умеет работать не только с обычным значением, но и с промисом.
Promise.reject(new Error("boom"))
.catch((error) => {
return Promise.resolve(error.message.toUpperCase());
})
.then((value) => {
console.log(value); // "BOOM"
});
Следующий then() выполнится только после завершения промиса, который был возвращён из catch().
Если в catch() выбросить ошибку, она пойдёт дальше
Иногда ошибку не нужно окончательно обрабатывать на текущем этапе. В таком случае её можно выбросить снова.
Promise.reject(new Error("boom"))
.catch(() => {
throw new Error("again");
})
.catch((error) => {
console.log(error.message); // "again"
});
То же самое произойдёт, если вернуть Promise.reject(...).
catch() не срабатывает у успешного промиса
Это полезно помнить, чтобы не ждать лишнего поведения.
Promise.resolve(1)
.catch(() => {
console.log("Не выполнится");
})
.then((value) => {
console.log(value); // 1
});
Если ошибки не было, catch() просто пропускается.
catch() - это сокращённая запись для then(undefined, onRejected)
По поведению эти варианты эквивалентны:
Promise.reject("x").then(undefined, (error) => {
console.log("then", error); // "x"
});
Promise.reject("x").catch((error) => {
console.log("catch", error); // "x"
});
Но catch() обычно читается проще, особенно в длинных цепочках.
catch() не меняет исходный промис
Метод создаёт новый промис и продолжает цепочку, но сам исходный объект не изменяет.
const promise = Promise.reject(new Error("boom"));
const handled = promise.catch((error) => error.message);
console.log(promise === handled); // false
handled.then((value) => {
console.log(value); // "boom"
});
Это важно, потому что один и тот же исходный промис технически можно обрабатывать разными способами.
Когда это полезно
catch() особенно удобен, когда:
- нужно обработать ошибку запроса, загрузки или другой асинхронной операции
- нужно поймать ошибку из одного из
then()в цепочке - нужно показать запасной результат вместо падения
- вы хотите централизованно завершить цепочку обработкой ошибки
Пример:
function loadUser() {
return Promise.reject(new Error("Пользователь не найден"));
}
loadUser()
.catch((error) => {
console.log(error.message); // "Пользователь не найден"
return { id: 0, name: "Гость" };
})
.then((user) => {
console.log(user.name); // "Гость"
});
Частые ошибки
Думать, что catch() возвращает не промис, а обычное значение
const result = Promise.reject(new Error("boom"))
.catch((error) => error.message);
console.log(result instanceof Promise); // true
Результат catch() - это всегда новый промис.
Ожидать, что catch() сработает без ошибки
Promise.resolve("ok")
.catch((error) => {
console.log(error);
})
.then((value) => {
console.log(value); // "ok"
});
Если промис завершился успешно, обработчик catch() не вызывается.
Забывать, что возврат из catch() восстанавливает цепочку
Promise.reject(new Error("boom"))
.catch((error) => {
console.log(error.message); // "boom"
return "fixed";
})
.then((value) => {
console.log(value); // "fixed"
});
После return ошибка считается обработанной, и дальше цепочка идёт как успешная.
Думать, что catch() поймает вообще любую асинхронную ошибку
Promise.resolve()
.then(() => {
setTimeout(() => {
throw new Error("boom");
}, 0);
})
.catch((error) => {
console.log("Не сработает", error);
});
Здесь ошибка возникает уже внутри колбэка setTimeout(), то есть вне текущей цепочки промисов. Такой случай нужно обрабатывать отдельно.
Случайно проглатывать ошибку, когда её нужно пробросить дальше
Promise.reject(new Error("boom"))
.catch((error) => {
console.log(error.message); // "boom"
})
.then(() => {
console.log("Цепочка пошла дальше как успешная");
});
Если после обработки ошибки нужно оставить цепочку в состоянии ошибки, ошибку надо выбросить снова или вернуть Promise.reject(...).
Быстрая памятка
catch()вызывается у объектаPromise- метод принимает обработчик ошибки
catch()всегда возвращает новый промис- метод перехватывает отклонённый промис
- метод перехватывает ошибки из предыдущих
then() - если вернуть значение, цепочка продолжится как успешная
- если вернуть промис, цепочка подождёт его завершения
- если выбросить ошибку, она пойдёт в следующий
catch() - у успешного промиса
catch()не срабатывает catch()по смыслу равенthen(undefined, onRejected)
Коротко
catch() в JavaScript нужен, чтобы обработать ошибку промиса и при необходимости продолжить цепочку дальше. Главное, что стоит запомнить: метод всегда возвращает новый промис, ловит ошибки из предыдущих шагов цепочки, может восстановить выполнение через return, а для ошибок вне цепочки промисов сам по себе уже не помогает.