react-doctor/async-await-in-loop
Collect the items and use `await Promise.all(items.map(...))` to run independent operations concurrently
- Category: Performance
- Severity: warn
- Source:
oxlint-plugin-react-doctor - Framework: global
- Enabled when: always
Validation prompt
Use this to decide whether a fired diagnostic is real or a false positive.
Confirm the awaits in the loop body call independent operations. The rule already skips sleep-like calls (sleep, delay, setTimeout, throttle), for await (...of) iterators, and loop-carried dependencies where one iteration assigns a variable the next iteration's await argument reads. False positive only if iterations must complete in order for correctness — ordered DB writes, cumulative state, rate-limited APIs, or retries that depend on the prior outcome.
Fix prompt
Use this once validation confirms the diagnostic is real.
Collect the items, then await Promise.all(items.map(async (item) => doWork(item))) to fan them out concurrently. If you need bounded concurrency, use a small p-limit-style queue. .forEach(async ...) is especially broken — return values are dropped so awaits don't actually wait; switch to a for...of loop or Promise.all over .map. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
Related rules
More Performance rules from the rules reference:
react-doctor/async-defer-await: Move the `await` after the synchronous early-return guard so the skip path stays fastreact-doctor/async-parallel: Use `const [a, b] = await Promise.all([fetchA(), fetchB()])` to run independent operations concurrentlyreact-doctor/client-passive-event-listeners: Add `{ passive: true }` as the third argument: `addEventListener('scroll', handler, { passive: true })`. Only do this if the handler does NOT call `event.preventDefault()` — passive listeners silently ignore `preventDefault()`, which breaks features like pull-to-refresh suppression, custom gestures, and nested-scroll containment.react-doctor/js-async-reduce-without-awaited-acc: Await the accumulator inside an async .reduce reducer: const acc = await previous; ...; return acc;react-doctor/js-batch-dom-css: Batch DOM/CSS reads and writes — interleaving them inside a loop causes layout thrashing. Read first, then write