#Что происходит и как распознать неисправность
Если функция возвращает new Promise, но на одном пути выполнения не вызывается ни resolve, ни reject, ожидание await продолжается бесконечно. Ошибка отличается от HTTP-задержки: даже когда исходная операция закончена, обещание не переходит в состояние fulfilled/rejected.
#Как отличить причины
| Возможная причина | Как узнать | Что делать |
|---|---|---|
| Ветка if выходит без завершенияРанний return или else не вызывает resolve/reject. | В определённом сценарии then/catch вообще не исполняются. | Обработайте все ветки, при ошибке вызывайте reject. |
| Исключение в отложенном обработчикеОшибка внутри setTimeout/event-handler не всегда связана с внешним исполнителем Promise. | В консоли есть uncaught error, Promise всё ещё pending. | Используйте reject внутри обработчика либо async-функцию без лишнего new Promise. |
| Событие никогда не приходитОбещание ждёт событие, которое не гарантировано. | Незавершённая загрузка или отсутствующий listener. | Добавьте обоснованный таймаут, очистку и отказ по ошибке. |
#Пошаговая диагностика и исправление
- Проверьте, где ожидается PromiseВ редакторе найдите строку
await myFunction()или.then(...), после которой нет вывода. Добавьте временныеconsole.log("до")иconsole.log("после")вокруг ожидания, а внутри исполнителя Promise — лог по каждой ветке. Воспроизведите случай, когдапослене появляется; не печатайте приватные данные. - Найдите все exit-пути внутри new PromiseОткройте функцию, выполняющую
return new Promise((resolve, reject) => {...}). Проверьтеif/else,return, обработчики ошибок и события: в каждом конечном исходе должен быть вызов resolve или reject. Самоreturnиз callback не разрешает Promise. Отметьте конкретную ветку, где управление теряется. - Разберите ошибки в отложенных callbackЕсли внутри
setTimeout, обработчика события или callback библиотека может бросить исключение, перехватите его в самом callback и передайте в reject. Исключение, возникшее после завершения конструктора Promise, не обязательно попадёт в автоматический reject его исполнителя. - Удалите лишнюю обёртку, если уже есть PromiseЕсли
fetch, async-функция или библиотечный вызов уже возвращают Promise, вместо новогоnew Promiseпростоreturn fetch(...)илиreturn await ...внутри функцииasync function.return awaitприменяйте, например, когда нужно перехватить ошибку локальнымtry/catch. Это упрощает обработку ошибок и исключает забытый resolve. - Добавьте таймаут только для операции, которая может зависнутьДля внешнего запроса используйте AbortController и допустимое бизнес-правилами время ожидания; передавайте его signal в fetch. По истечении срока отменяйте запрос, выводите ясную ошибку, очищайте таймер и обработчики. Не выбирайте произвольные 1–2 секунды для операций, которые законно выполняются дольше.
- Привяжите reject к реальному состоянию ошибкиВ случае отказа файла или запроса сохраняйте причину (
reject(error)), а неresolve(undefined), если отсутствие результата является ошибкой по контракту. Вызывающий код должен иметь try/catch для await либо.catch. Если ошибку намеренно обрабатывают выше, не скрывайте её пустым обработчиком. - Проверьте все исходы и очисткуВ тестовой среде смоделируйте удачную операцию, сетевую ошибку, ранний return и таймаут. Для каждого сценария await должен завершаться значением либо управляемым исключением, а слушатели/таймеры должны очищаться. При неверной бизнес-логике верните копию функции и согласуйте контракт завершения.
#Команды и примеры для проверки
function getResult(enabled) {
return new Promise((resolve, reject) => {
if (!enabled) {
reject(new Error("Операция запрещена"));
return;
}
setTimeout(() => resolve("готово"), 100);
});
}
getResult(true).then(console.log).catch(console.error);#Как проверить результат и что делать дальше
Промис больше не остаётся pending в завершившейся операции: любой конечный исход обрабатывается как успех, ошибка или отмена. Не подменяйте исправление постоянным опросом состояния без предела.