External API calls inside loops use Promise.all batching
Why it matters
Sequential await fetch( inside a loop over a non-trivial collection is one of the most common AI-generated code patterns that degrades gracefully in testing and catastrophically in production. Each awaited call holds a thread-of-execution and an HTTP connection open until it resolves; a list of 200 items becomes 200 serialized round-trips, inflating p99 latency from 200ms to 40 seconds. The pattern also prevents proper error isolation — a single slow upstream response blocks everything behind it. On metered hosting (Vercel function duration, Lambda invocation time), sequential loops inflate compute costs linearly with list size.
Severity rationale
High because sequential awaits in loops inflate response latency and compute billing linearly with input size, with no platform-level protection against runaway iteration.
Remediation
Replace the sequential loop with Promise.all or pMap for bounded parallelism. In src/lib/sync.ts or whichever file contains the offending pattern:
// Bad: 200 items × 200ms = 40 seconds
for (const user of users) {
await fetch(`/api/sync/${user.id}`)
}
// Good: 200 items at concurrency 10 ≈ 4 seconds
import pMap from 'p-map'
await pMap(
users,
async (user) => fetch(`/api/sync/${user.id}`, { signal: AbortSignal.timeout(5000) }),
{ concurrency: 10 },
)
Always pair with input length validation so the array itself is bounded before any parallelism runs.
Detection
-
ID:
external-api-loops-batched -
Severity:
high -
What to look for: Walk source files for
await fetch(,await axios.,await ky.,await got(calls insidefor/while/forEach/.map(/.filter(/.reduce(loops. For each match, count all loop-bound external API call sites and verify the call is insidePromise.all([...])orPromise.allSettled([...])OR is intentionally rate-paced. EXCLUDE calls with asetTimeout/sleep/delaybetween them. -
Pass criteria: 100% of loop-bound external API calls use
Promise.all/Promise.allSettledOR have intentional pacing. Report: "X loop-bound API call sites, Y batched, 0 sequential." -
Fail criteria: At least 1 sequential
await fetch(in a loop with no batching or pacing. -
Skip (N/A) when: No external API call patterns found.
-
Detail on fail:
"1 sequential loop fetch: src/lib/sync.ts loops 'for (const u of users) { await fetch(\\/api/sync/${u.id}\) }' — N+1 latency" -
Remediation: Sequential awaits in loops are slow AND wasteful. Batch with bounded concurrency:
// Bad: serial for (const user of users) { await fetch(`/api/sync/${user.id}`) } // Good: parallel with concurrency cap import pMap from 'p-map' await pMap(users, async (u) => fetch(`/api/sync/${u.id}`), { concurrency: 10 })
External references
- iso-25010:2011 · performance-efficiency
Taxons
History
- 2026-04-18·v1.0.0·Initial import from ai-slop-cost-bombs·automated