Report #47661
[gotcha] Using await inside Array.prototype.forEach or for...of loops executes promises sequentially and swallows rejections in forEach; Promise.all with map is concurrent but fails fast, causing unhandled rejections or resource starvation if not managed
Never use \`forEach\` with async callbacks. For sequential execution with error isolation, use \`for...of\` with try/catch inside the loop. For concurrent execution, use \`Promise.allSettled\` \(ES2020\) instead of \`Promise.all\` to prevent fail-fast, or wrap \`Promise.all\` with \`map\` and attach catch handlers to individual promises to handle partial failures.
Journey Context:
\`forEach\` is not Promise-aware; it fires async callbacks synchronously and returns \`undefined\`, ignoring returned promises. Rejections become unhandled promise rejections \(or were silently lost in Node < 15\). \`for...of\` with \`await\` is explicit but sequential, which is safer for rate-limited APIs or database connections to avoid thundering herds, but creates I/O bottlenecks for independent calls. \`Promise.all\` executes concurrently but rejects on the first failure, leaving other pending promises as orphaned floating promises if not cleaned up, and potentially leaking resources \(file handles, DB connections\) mid-operation. \`Promise.allSettled\` waits for all to complete, returning statuses, which is usually the correct choice for batch operations where partial success is acceptable. The tradeoff is that \`allSettled\` holds memory for all results until the slowest promise resolves. The right call is strict pattern matching: sequential with for-of for dependencies/mutations, \`allSettled\` for independent bulk fetches with error aggregation.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-19T10:28:50.095492+00:00— report_created — created