В чем отличие асинхронного подхода от синхронного
Что проверяют
Понимаете ли вы модель выполнения JavaScript: один поток, стек вызовов и цикл событий. Главный маркер: не скажете ли вы, что асинхронный код выполняется параллельно. Следом часто дают задачку на порядок вывода с setTimeout, Promise.then и console.log.
Как отвечать
Синхронный код выполняется строка за строкой в стеке вызовов, и пока он работает, основной поток занят: браузер не отрисовывает кадры и не обрабатывает клики. Цикл на 200 мс означает 200 мс замершего интерфейса.
Асинхронный подход не добавляет потоков. Долгую операцию (fetch, таймер, чтение файла) выполняет окружение, браузер или Node.js, а JavaScript получает колбэк, когда результат готов. Дальше работает цикл событий:
- макрозадачи:
setTimeout, события DOM,MessageChannel. Берутся из очереди по одной; - микрозадачи:
Promise.then, продолжение послеawait,queueMicrotask. Выполняются все сразу после текущей задачи, до следующей макрозадачи и до отрисовки; - async/await: синтаксис над промисами.
awaitприостанавливает функцию, но не поток, остальной код продолжает работать.
Поэтому console.log(1); setTimeout(() => console.log(2)); Promise.resolve().then(() => console.log(3)); console.log(4) выведет 1, 4, 3, 2.
Отдельно скажите про тяжёлые вычисления: асинхронность от них не спасает, async-функция с циклом на миллион итераций блокирует поток точно так же. Для этого есть Web Worker или разбиение работы на части. А ещё асинхронность приносит свои проблемы: гонки ответов в поиске решают через AbortController, независимые запросы запускают вместе через Promise.all.
Где спотыкаются
Говорят, что асинхронные задачи идут параллельно в разных потоках. Параллельно работают только операции окружения, ваш код всегда выполняется по очереди.
Пишут array.forEach(async ...) и ждут, что код дождётся всех итераций. forEach промисы игнорирует, нужен for...of с await или Promise.all с map.
Ставят await последовательно на независимые запросы. Три запроса по 300 мс занимают почти секунду вместо 300 мс.