Как предсказать вывод консольных логов при выполнении асинхронного кода в JavaScript
Что проверяют
Знаете ли вы модель событийного цикла настолько, чтобы пройти по коду руками и не гадать. Следом дают вариант посложнее: с async/await, цепочками .then или вопросом, что изменится в Node.js.
Как отвечать
Сформулируйте алгоритм и примените его построчно:
- синхронный код: выполняется целиком до конца скрипта. Сюда входит и функция-исполнитель
new Promise(...), и телоasync-функции до первогоawait; - микрозадачи: колбэки
.then/.catch/.finally, продолжение послеawait,queueMicrotask. Очередь опустошается полностью, включая микрозадачи, добавленные по ходу; - макрозадачи:
setTimeout,setInterval, события,postMessage. После каждой макрозадачи снова разбираются все микрозадачи, а браузер может отрисовать кадр.
Разберите пример вслух:
console.log(1);
setTimeout(() => console.log(2));
Promise.resolve().then(() => console.log(3));
(async () => { console.log(4); await null; console.log(5); })();
console.log(6);Вывод: 1, 4, 6, 3, 5, 2. Синхронно печатаются 1, 4 и 6, потом две микрозадачи в порядке постановки, таймер последним. await null откладывает продолжение, даже когда ждать нечего.
Если спросят про Node.js: process.nextTick выполняется раньше промисов, а порядок setTimeout(0) и setImmediate в основном модуле не определён. Внутри колбэка ввода-вывода первым идёт setImmediate.
Где спотыкаются
Считают, что setTimeout(fn, 0) выполнится сразу или раньше промиса. Ноль означает только «не раньше чем», и таймер всегда ждёт, пока опустеет очередь микрозадач.
Думают, что код внутри new Promise((resolve) => ...) асинхронный. Исполнитель вызывается синхронно, асинхронны только обработчики результата.
Путаются в двух параллельных цепочках. Каждый .then ставит отдельную микрозадачу, поэтому для уже выполненных a и b цепочки a.then(f1).then(f2) и b.then(g1).then(g2) отработают в порядке f1, g1, f2, g2, а не одна за другой.