Defer await until needed
Условная оптимизация — await только там, где данные реально используются
❌ Неправильно (всегда загружает permissions):
async function updateResource(resourceId: string, userId: string) {
const permissions = await fetchPermissions(userId)
const resource = await getResource(resourceId)
if (!resource) {
return { error: 'Not found' }
}
if (!permissions.canEdit) {
return { error: 'Forbidden' }
}
return await updateResourceData(resource, permissions)
}
✅ Правильно (permissions грузим только если ресурс найден):
async function updateResource(resourceId: string, userId: string) {
const resource = await getResource(resourceId)
if (!resource) {
return { error: 'Not found' }
}
const permissions = await fetchPermissions(userId)
if (!permissions.canEdit) {
return { error: 'Forbidden' }
}
return await updateResourceData(resource, permissions)
}
Простой пример с early return:
// ❌ Неправильно
async function handleRequest(userId: string, skipProcessing: boolean) {
const userData = await fetchUserData(userId)
if (skipProcessing) {
return { skipped: true }
}
return processUserData(userData)
}
// ✅ Правильно
async function handleRequest(userId: string, skipProcessing: boolean) {
if (skipProcessing) {
return { skipped: true }
}
const userData = await fetchUserData(userId)
return processUserData(userData)
}
Мои мысли: Всё просто: если что-то можно не делать — не делайте. Либо выполните что-то более полезное, либо просто сэкономьте ресурсы. Это правило применимо во множестве ситуаций, а не только в React/фронтенде.
---
Strategic Suspense boundaries
UI-оптимизация — layout показывается сразу, данные грузятся внутри
❌ Неправильно (весь layout ждёт данные):
async function Page() {
const data = await fetchData() // блокирует всю страницу
return (
<div>
<div>Sidebar</div>
<div>Header</div>
<div>
<DataDisplay data={data} />
</div>
<div>Footer</div>
</div>
)
}
✅ Правильно (Sidebar/Header/Footer сразу; ждёт только DataDisplay):
function Page() {
return (
<div>
<div>Sidebar</div>
<div>Header</div>
<div>
<Suspense fallback={<Skeleton />}>
<DataDisplay />
</Suspense>
</div>
<div>Footer</div>
</div>
)
}
async function DataDisplay() {
const data = await fetchData() // блокирует только этот компонент
return <div>{data.content}</div>
}
✅ Продвинутый вариант (несколько компонентов используют одни данные):
function Page() {
const dataPromise = fetchData() // стартует сразу, но не await
return (
<div>
<div>Sidebar</div>
<div>Header</div>
<Suspense fallback={<Skeleton />}>
<DataDisplay dataPromise={dataPromise} />
<DataSummary dataPromise={dataPromise} />
</Suspense>
<div>Footer</div>
</div>
)
}
function DataDisplay({ dataPromise }: { dataPromise: Promise<Data> }) {
const data = use(dataPromise)
return <div>{data.content}</div>
}
function DataSummary({ dataPromise }: { dataPromise: Promise<Data> }) {
const data = use(dataPromise)
return <div>{data.summary}</div>
}
Мои мысли: Правильная композиция решает огромное количество проблем — и тут мы снова упираемся в это. Организуйте интерфейс так, чтобы как можно быстрее начать отдавать пользователю его части (без перегибов). При этом важно следить за кэшем и дедупликацией: при сильном разбиении легко дернуть один и тот же запрос несколько раз — это и лишние ресурсы, и риск неконсистентного интерфейса (ответы могут отличаться). Второй вариант решения с прокидыванием проммисов в пропы мне не нравится...