useEffect выполняется синхронно или асинхронно?
Хуки
Сам колбэк useEffect — обычная синхронная функция, но вызывается он асинхронно относительно рендера: после того как изменения применены к DOM и браузер отрисовал кадр.
Порядок событий
рендер (вызов компонента)
→ commit (правки DOM)
→ браузер отрисовывает кадр
→ useEffectПоэтому эффект не блокирует отрисовку: пользователь видит обновлённый UI, даже если в эффекте тяжёлая работа. Для сравнения, useLayoutEffect вызывается синхронно между коммитом и отрисовкой — блокируя кадр.
Проверка на практике
useEffect(() => {
console.log('effect'); // третьим
});
useLayoutEffect(() => {
console.log('layout effect'); // вторым
});
console.log('render'); // первымАсинхронный код внутри эффекта
Сам колбэк не может быть async (он обязан вернуть cleanup-функцию или undefined, а async возвращает Promise). Асинхронность оформляется внутри:
useEffect(() => {
let cancelled = false;
(async () => {
const data = await loadData(id);
if (!cancelled) setData(data);
})();
return () => { cancelled = true; };
}, [id]);Нюансы
- «Асинхронно» здесь означает «не в потоке рендера», а не setTimeout: React планирует эффекты сам, обычно сразу после отрисовки.
- Если в эффекте синхронно вызвать setState, React может выполнить повторный рендер до показа кадра — отсюда возможные «двойные» рендеры при неправильной работе с состоянием в эффектах.
Коротко для собеседования
useEffect выполняется асинхронно относительно рендера — после коммита в DOM и отрисовки кадра, не блокируя её (в отличие от синхронного useLayoutEffect). Сам колбэк — синхронная функция: async напрямую нельзя, асинхронный код оформляется внутри с флагом отмены в cleanup.