Интервью-вопрос
Что принимает метод reject в Promise
reject принимает одну причину отклонения Promise. На практике важно не только знать, что тип может быть любым, но и объяснить, почему в приложении обычно передают Error.
- Добавлен
- Редакция
Подготовьте короткий ответ и пару деталей на случай уточняющих вопросов.
Мини-квиз
Проверка перед разбором
Несколько быстрых вопросов перед разбором. Так проще поймать места, которые только кажутся понятными.
Вопрос 1 из 50 правильно
Разбор
Разобраться, а не зазубрить
Дальше разбираем суть, типичные уточнения и места, где легко сказать лишнее или перепутать термины.
Базовая идея
У Promise есть два финальных состояния: fulfilled и rejected. reject(reason) переводит Promise в состояние rejected. Переданное значение становится причиной отклонения. Это значение обычно называют reason.
На интервью стоит сказать аккуратно: JavaScript не требует, чтобы reason был именно Error. Но если вы передаете строку или произвольный объект, вы сами отвечаете за то, как потом это обработать, залогировать и показать пользователю.
Хороший короткий ответ:
reject принимает один аргумент, причину отклонения Promise. Передать можно любое значение, но на практике лучше передавать Error или свой класс ошибки, чтобы не потерять stack и иметь единый формат обработки.
Что попадет в catch
Значение из reject попадает в обработчик ошибок без преобразования. Если вы передали строку, в catch будет строка. Если передали Error, будет объект ошибки.
Promise.reject(new Error("Failed to load user"))
.catch((error) => {
console.log(error.message);
console.log(error.stack);
});
Promise.reject("Failed to load user")
.catch((reason) => {
console.log(reason); // строка, не Error
});Первый вариант лучше для приложения. Его проще обрабатывать одинаково во всех местах: можно прочитать message, отправить ошибку в мониторинг и сохранить стек вызовов. Второй вариант допустим для маленького примера, но в кодовой базе быстро приводит к проверкам вида typeof reason === "string".
Как выбрать, что передавать
На практике вопрос не в том, что разрешено. Важнее, какой контракт будет удобен вашему UI. Если компоненту нужно понять, что случилось, передавайте структурированную ошибку. Если это обычный результат, не маскируйте его под ошибку.
Как выбрать значение для reject
Отклоняйте Promise через Error или свой класс ошибки.Добавьте поля status, code, details в Error-наследник.Верните успешный результат с явным статусом, не используйте reject.Отличайте отмену от сбоя и не показывайте ее как аварийную ошибку.Пример для API-клиента
Плохой вариант: отклонить Promise строкой и потерять статус ответа.
async function loadUser(id) {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
return Promise.reject("Request failed");
}
return response.json();
}На практике компонент не поймет, это 401, 404 или 500. В итоге UI может показать общий toast там, где нужно отправить пользователя на логин или показать состояние "пользователь не найден".
Безопаснее нормализовать ошибку.
class HttpError extends Error {
constructor(message, { status, url }) {
super(message);
this.name = "HttpError";
this.status = status;
this.url = url;
}
}
async function loadUser(id) {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new HttpError("Failed to load user", {
status: response.status,
url: response.url,
});
}
return response.json();
}Здесь используется throw, но внутри async функции результат будет тем же для вызывающего кода: функция вернет rejected Promise. При этом ошибка сохранит тип, сообщение и статус.
reject и async/await
Когда вы пишете await для Promise, который был отклонен через reject, управление переходит в catch. Поэтому при ответе можно связать Promise-стиль и async/await.
async function renderUser() {
try {
const user = await loadUser("42");
return user.name;
} catch (error) {
if (error instanceof HttpError && error.status === 404) {
return "User not found";
}
throw error;
}
}Практический вывод для фронтенда: обработчик должен не только вывести ошибку в консоль. Обычно он еще снимает loading, показывает fallback, делает rollback optimistic UI или решает, можно ли повторить запрос.
Отмена запроса и состояние UI
Во фронтенде rejected Promise не всегда значит аварийный сбой. Например, запрос мог быть отменен, потому что пользователь ушел со страницы или быстро ввел новый текст поиска. Если обработать отмену как обычную ошибку, UI покажет ложный toast, перетрет новые данные старым состоянием или оставит кнопку в loading.
Опасный вариант: любой catch переводит экран в ошибку.
async function submit(signal) {
setLoading(true);
try {
await fetch("/api/profile", { signal });
} catch (error) {
setError("Не удалось сохранить профиль");
} finally {
setLoading(false);
}
}Если запрос отменили намеренно, пользователь все равно увидит сообщение об ошибке. Безопаснее отличить отмену от сбоя и оставить catch для реальной проблемы.
async function submit(signal) {
setLoading(true);
try {
await fetch("/api/profile", { signal });
setError(null);
} catch (error) {
if (error?.name === "AbortError") {
return;
}
setError("Не удалось сохранить профиль");
} finally {
setLoading(false);
}
}На интервью это можно сформулировать так: значение из reject должно помогать UI принять решение. Настоящую ошибку показываем и логируем. Ожидаемую отмену обрабатываем тихо, но состояние загрузки все равно закрываем.
Что важно для UI
Ошибка в Promise почти всегда связана с состоянием интерфейса. Если вы отклонили Promise, но не обработали причину, пользователь может остаться со спиннером, заблокированной кнопкой или пустым экраном.
- 1Создать Error с понятным message
- 2Добавить контекст, если он нужен UI
- 3Отклонить Promise или бросить ошибку
- 4Обработать в catch или try/catch
- 5Снять loading в finally или в явной ветке
- 6Показать подходящее состояние без ложного error state
- 1Передать строку без стека
- 2Смешать ошибку и обычный результат
- 3Не поставить catch
- 4Оставить компонент в loading
- 5Потерять причину сбоя в логах
Практический вывод
На интервью не ограничивайтесь фразой "reject принимает ошибку". Это звучит неполно, потому что по факту он принимает любое значение. Лучше показать оба уровня ответа: формальный контракт и хорошую практику.
Сильная формулировка:
reject принимает один аргумент, reason отклонения. Тип не ограничен, но я бы передавал Error или свой Error-наследник. Так catch получает нормальную ошибку со stack и контекстом, а UI может обработать разные сбои без догадок по строкам.
Если вас спросят про строку, не спорьте, что она запрещена. Скажите, что она допустима, но хуже для поддержки и наблюдаемости.
Частые ошибки
Где обычно ошибаются
Проверьте формулировки, которые звучат уверенно, но на интервью быстро выдают пробелы.
- 1
Передавать строку вместо Error в рабочем коде
Строка допустима по спецификации, но у нее нетstackи стабильной структуры. Из-за этого вам придется добавлять проверки на типы в логировании и обработчиках ошибок. В рабочем коде безопаснее передаватьnew Error("...")или свой класс ошибки. - 2
Использовать reject для обычных результатов
Если пользователь закрыл модальное окно или выбрал пустой фильтр, это не всегда ошибка. Когда такие случаи уходят вcatch, ваш код начинает показывать лишние toasts и считать нормальное поведение сбоем. Лучше вернуть объект результата, например{ status: "cancelled" }. - 3
Не обрабатывать отклонение в UI
Rejected Promise безcatchчасто оставляет кнопку заблокированной, спиннер включенным или форму без сообщения. На интервью скажите не только про синтаксис, но и про состояние интерфейса. В обработчике ошибки нужно снять loading и показать понятную реакцию. - 4
Терять контекст HTTP-ошибки
Если сделатьreject(new Error("Request failed"))без статуса и тела ответа, UI не отличит авторизацию от ошибки сервера. В API-клиенте лучше сохранитьstatus,urlи безопасную часть ответа. Тогда вы сможете по-разному обработать 401, 404 и 500. - 5
Считать отмененный запрос обычной ошибкой
При уходе со страницы или новом поисковом запросе старый запрос могут отменить. Если такую отмену показывать как ошибку, пользователь увидит ложный toast или error state. ПроверяйтеAbortErrorили свой признак отмены, затем отдельно снимайтеloading.
Follow-up
Что могут спросить дальше
Короткие ответы на вопросы, которыми проверяют понимание reject, catch и async/await.
Живые ответы
Видео с похожим вопросом
Если найдем публичные интервью с таким вопросом, добавим их сюда. Их удобно смотреть после теории, чтобы свериться с живыми ответами.
Пока видео нет. Когда появятся подходящие публичные интервью, добавим их в этот блок, чтобы можно было сравнить разбор с тем, как отвечают реальные кандидаты.
Что использовали раньше до появления Promise в JavaScript 😎
До Promise асинхронность чаще строили на callback-функциях, событиях, Deferred-объектах и библиотеках для контроля потока. На странице разбираем, что сказать на интервью и какие ограничения этих подходов важно назвать.
Что сработает первое, setInterval или Promise 😎
Если setInterval и Promise запланированы в одном синхронном участке кода, обработчик Promise выполнится раньше. На странице разбираем очереди задач, микрозадачи и практический риск для таймеров во фронтенде.