--- name: frontend-async-best-practices description: Async/await and Promise optimization guidelines. Use when writing, reviewing, or refactoring asynchronous code to eliminate waterfalls and maximize parallelism. Triggers on tasks involving data fetching, loaders, actions, or Promise handling. --- # Async Best Practices Performance optimization patterns for asynchronous JavaScript code. Contains 5 rules focused on eliminating request waterfalls and maximizing parallelism. **Impact: CRITICAL** - Waterfalls are the #1 performance killer. Each sequential await adds full network latency. ## When to Apply Reference these guidelines when: - Writing Remix loaders or actions - Implementing data fetching logic - Working with multiple async operations - Reviewing code for waterfall patterns - Optimizing response times ## Rules Summary ### parallel (CRITICAL) — @rules/parallel.md Use `Promise.all()` for independent operations. ```typescript // Bad: 3 sequential round trips const user = await fetchUser(); const posts = await fetchPosts(); const comments = await fetchComments(); // Good: 1 parallel round trip const [user, posts, comments] = await Promise.all([ fetchUser(), fetchPosts(), fetchComments(), ]); ``` ### defer-await (HIGH) — @rules/defer-await.md Move await into branches where actually used. ```typescript // Bad: always waits even when skipping async function handle(skip: boolean) { let data = await fetchData(); if (skip) return { skipped: true }; return process(data); } // Good: only waits when needed async function handle(skip: boolean) { if (skip) return { skipped: true }; let data = await fetchData(); return process(data); } ``` ### dependencies (CRITICAL) — @rules/dependencies.md Chain dependent operations, parallelize independent ones. ```typescript // Bad: profile waits for config unnecessarily const [user, config] = await Promise.all([fetchUser(), fetchConfig()]); const profile = await fetchProfile(user.id); // Good: profile starts as soon as user resolves const userPromise = fetchUser(); const profilePromise = userPromise.then((user) => fetchProfile(user.id)); const [user, config, profile] = await Promise.all([ userPromise, fetchConfig(), profilePromise, ]); ``` ### api-routes (CRITICAL) — @rules/api-routes.md Start promises early, await late in loaders. ```typescript // Bad: sequential execution export async function loader() { let session = await auth(); let config = await fetchConfig(); return { session, config }; } // Good: parallel execution export async function loader() { let sessionPromise = auth(); let configPromise = fetchConfig(); const [session, config] = await Promise.all([sessionPromise, configPromise]); return { session, config }; } ``` ### suspense-boundaries (HIGH) — @rules/suspense-boundaries.md Use Suspense to show UI immediately while data loads. ```tsx // Bad: entire page blocked by data async function Page() { let data = await fetchData(); return ( ); } // Good: layout shows immediately, content streams in function Page() { return ( }> ); } ```