Зачем нужен промис в JavaScript и почему возникла необходимость
Что проверяют
Понимаете ли вы, какую проблему решили промисы и как они работают внутри, а не только умеете писать async/await. Следом обычно дают задачку на порядок вывода с setTimeout, Promise.resolve и console.log или просят сравнить Promise.all и Promise.allSettled.
Как отвечать
До ES2015 асинхронность держалась на колбэках, и у них три проблемы:
- вложенность: последовательные запросы превращаются в «пирамиду», callback hell;
- ошибки:
try/catchне ловит исключение, брошенное в колбэке позже, каждый уровень проверяет ошибку вручную; - инверсия управления: вы отдаёте функцию чужому коду и не знаете, вызовут её один раз, дважды или никогда.
Промис это объект, представляющий будущий результат. Состояния три: pending, fulfilled, rejected, и из ожидания он выходит только один раз. Что это даёт:
- цепочки:
.thenвозвращает новый промис, значение или брошенная ошибка передаются дальше, вложенность исчезает; - единая обработка ошибок: один
.catchв конце ловит сбой на любом шаге; - предсказуемый порядок: обработчики всегда вызываются асинхронно, через очередь микрозадач, после текущего синхронного кода и раньше
setTimeout; - комбинаторы:
Promise.all,allSettled,race,any.
Промисы вошли в ES2015 на основе спецификации Promises/A+ и библиотек вроде Bluebird. async/await из ES2017 это синтаксис поверх тех же промисов. Отменить промис нельзя, для fetch используют AbortController.
Где спотыкаются
Думают, что промис делает код параллельным или запускает его в другом потоке. JavaScript однопоточный, а функция-исполнитель в new Promise(...) выполняется сразу и синхронно. Асинхронность дают браузерные API, промис только представляет их результат.
Забывают return внутри .then. Следующий шаг не ждёт вложенный запрос, а его ошибка остаётся необработанной и всплывает событием unhandledrejection.
Пишут await в цикле для независимых запросов, и они идут строго по очереди. Для параллельной загрузки нужен Promise.all, а если отдельные сбои допустимы, Promise.allSettled.