Как работает конструктор Promise, синхронность создания и асинхронность резолва в JavaScript
Что проверяют
Понимаете ли вы, что тело конструктора выполняется немедленно, и знаете ли, почему then асинхронен всегда.
Как отвечать
Функция, переданная в конструктор (её называют исполнителем), выполняется синхронно и немедленно, прямо во время создания промиса. Асинхронна не она, а доставка результата подписчикам.
console.log("1");
const p = new Promise((resolve) => {
console.log("2"); // выполнится сразу
resolve("значение");
});
p.then((v) => console.log("4", v));
console.log("3");
// 1, 2, 3, 4Видно, что «2» печатается до «3»: тело исполнителя это обычный синхронный код. А колбэк then попадает в очередь микрозадач и выполняется после того, как отработает весь текущий синхронный код.
Ключевые следствия:
thenасинхронен всегда, даже если промис уже разрешён в момент подписки. Это сделано намеренно: иначе одна и та же функция вела бы себя по-разному в зависимости от того, успел ли результат прийти, и код был бы непредсказуем;resolveиrejectсрабатывают один раз. Повторные вызовы игнорируются молча, состояние промиса необратимо;- исключение внутри исполнителя автоматически превращается в отклонение промиса. Но это верно только для синхронной части: исключение, брошенное позже внутри колбэка таймера, промис не поймает, и оно станет необработанным;
- промис начинает работу в момент создания, а не в момент подписки. Поэтому передавать в
Promise.allнадо созданные промисы, а для ограничения одновременности их оборачивают в функции.
Стоит назвать частый антипаттерн: обёртка уже возвращающей промис функции в новый new Promise. Это лишний уровень, в котором обычно теряется обработка ошибок. Конструктор нужен только для превращения в промис старого интерфейса с колбэками.
Где спотыкаются
Считают, что тело конструктора выполняется асинхронно.
Ждут, что then на уже разрешённом промисе выполнится синхронно.
Бросают исключение внутри асинхронного колбэка и ждут отклонения промиса.
Оборачивают промис в новый промис без надобности.