Является ли тело промиса или его колбэки синхронными?
Что проверяют
Знаете ли вы, в какой момент выполняется код внутри конструктора промиса и куда попадают колбэки. Вопрос про понимание событийного цикла и очереди микрозадач, а не про умение пересказать определение промиса.
Как отвечать
Ответ составной: тело промиса синхронно, колбэки асинхронны.
Функция-исполнитель, переданная в new Promise, вызывается немедленно и до конца, в том же стеке, где промис создавался. Ничего отложенного в ней нет: тяжёлый цикл внутри исполнителя заблокирует страницу ровно так же, как заблокировал бы вне промиса.
Колбэки then, catch и finally всегда попадают в очередь микрозадач и выполняются после того, как опустеет текущий синхронный стек. Даже если промис уже выполнен в момент подписки, колбэк не вызовется сразу.
console.log(1);
const p = new Promise((resolve) => {
console.log(2);
resolve();
});
setTimeout(() => console.log(5), 0);
p.then(() => console.log(4));
console.log(3);Порядок вывода тут 1, 2, 3, 4, 5: микрозадача идёт раньше таймера, потому что очередь микрозадач разбирается полностью перед следующей макрозадачей.
Асинхронная функция ведёт себя так же: тело выполняется синхронно до первого await, а остаток продолжается микрозадачей.
Где спотыкаются
Говорят, что промис выполняется в отдельном потоке. Поток один, промис лишь описывает, когда будет обработан результат, а параллельность даёт не он, а источник асинхронности: таймер, сеть, воркер.
Ждут ленивости. Работа стартует в момент создания промиса, а не при первом then, поэтому запрос уйдёт даже тогда, когда его результат никому не понадобится.
Бесконечно планируют микрозадачи внутри цепочки. Очередь микрозадач разбирается до конца перед отрисовкой, поэтому такая цепочка вешает интерфейс намертво, хотя ни одной синхронной петли в коде нет.