Как работать с Promise в цепочках
Что проверяют
Понимает ли кандидат, что then возвращает новый промис, как значение и ошибка идут по цепочке и почему цепочка плоская, а не вложенная. Обычно за этим следует вопрос про обработку ошибок и параллельный запуск.
Как отвечать
Промис это объект с тремя состояниями: pending, fulfilled, rejected. Переход происходит один раз и необратим.
Ключевое: then, catch и finally возвращают новый промис. Возврат обычного значения из then даёт выполненный промис с этим значением, возврат промиса заставляет цепочку дождаться его, брошенное исключение переводит следующее звено в rejected. Именно поэтому цепочка остаётся плоской:
fetchUser(id)
.then(user => fetchOrders(user.id)) // вернули промис, следующий then ждёт его
.then(orders => render(orders))
.catch(err => showError(err))
.finally(() => hideSpinner());Что стоит проговорить отдельно:
returnобязателен. Без него следующийthenполучитundefinedи не станет ждать асинхронную операцию, а ошибка внутри неё не попадёт вcatch.catch(fn)это сокращениеthen(undefined, fn). Он ловит отказ любого из предыдущих звеньев, но не последующих, поэтому его место в конце цепочки.- После
catchцепочка продолжается как успешная, если обработчик не бросил ошибку заново. Следующийthenвыполнится, и это регулярно удивляет. - Второй аргумент
then(onOk, onErr)не поймает ошибку, возникшую в самомonOk: отдельныйcatchниже поймает. finallyне получает значение и не меняет результат, просто пропускает его дальше. Но брошенная внутри него ошибка результат заменит.
Параллельный запуск, если операции независимы: Promise.all (падает по первой ошибке), Promise.allSettled (ждёт все и отдаёт статусы), Promise.race, Promise.any. Последовательный await в цикле там, где нужен параллелизм, замедляет работу кратно числу итераций.
Цепочка без catch даёт unhandledrejection в браузере и падение процесса в Node. Ещё один антипаттерн: оборачивать в new Promise то, что уже промис.
async/await это тот же механизм в другом синтаксисе: обработка через try/catch, а Promise.all остаётся нужен для параллельности. Мешать оба стиля в одной функции не стоит.
Где спотыкаются
Забывают return в then, из-за чего следующий шаг получает undefined, а ошибки не всплывают. Второе место: ставят catch в середине цепочки и потом не могут объяснить, почему после ошибки выполнились оставшиеся шаги.