В чем разница между async function и обычной функцией в JavaScript?
Что проверяют
Понимаете ли вы, что async меняет не синтаксис, а контракт функции: она всегда возвращает промис и умеет приостанавливаться на await. Следом обычно спрашивают, в каком порядке выполнится код до и после await, или просят разобрать пример с forEach и асинхронным колбэком.
Как отвечать
Главное отличие и его следствия:
- возвращаемое значение:
asyncфункция всегда возвращаетPromise.return 5превращается в промис, который выполнится со значением 5, а возвращённый промис второй раз не оборачивается, его результат просто переходит наружу; - исключения:
throwвнутри не вылетает синхронно, а переводит промис в состояние rejected. Поймать ошибку можно черезtry/catchвокругawaitили через.catch()в вызывающем коде; await: доступен только внутриasyncфункции и на верхнем уровне ES-модулей. Он приостанавливает именно эту функцию, а не поток: продолжение ставится в очередь микрозадач, а браузер тем временем рисует и обрабатывает события;- синхронная часть: код до первого
awaitвыполняется сразу при вызове. Асинхронность начинается только с первогоawait; - ограничения:
asyncфункция не может быть конструктором,newвыдастTypeError. Бывают асинхронные стрелки, методы и генераторы (async function*дляfor await).
Покажите, что async/await это синтаксис поверх промисов, а не новая модель: любой такой код переписывается на .then(). Для React пригодится практический пример: колбэк useEffect нельзя делать async, эффект должен вернуть функцию очистки или ничего, а не промис. Асинхронную функцию объявляют внутри эффекта и вызывают.
Где спотыкаются
Говорят, что await «блокирует поток». Он приостанавливает только текущую функцию, event loop продолжает работать.
Пишут array.forEach(async (x) => { await save(x) }) и ждут последовательного выполнения. forEach промисы не ждёт: для очереди нужен for...of с await, для параллели Promise.all с map.
Ставят await в цикле там, где запросы независимы, и получают последовательную загрузку вместо параллельной. Или забывают await совсем и ловят необработанный rejected промис вместо ошибки в try/catch.