Using Async.parallel my parameter's life-span isn't outliving the asynchronous calls in NodeJS using MongoDB
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
When using async.parallel with MongoDB in Node.js, variables declared in the outer scope can appear stale or undefined inside the parallel callbacks. This happens because JavaScript closures capture variable references, not values. If the variable changes before the async callback fires, the callback sees the changed value. The fix is to use let (block scoping), const, or pass values as function arguments to freeze them at capture time.
The Problem
All three callbacks print "cherry" because var item is function-scoped. By the time the callbacks execute, the loop has finished and item holds the last value.
Fix 1: Use let Instead of var
let and const create a new binding for each loop iteration. Each callback captures its own item.
Fix 2: Use Array.map
map creates a new function scope for each element. The item parameter is unique to each iteration.
Fix 3: Use IIFE (Immediately Invoked Function Expression)
For legacy code that must use var:
The IIFE creates a new scope, freezing the value of item at the time of invocation.
Fix 4: Use Promises and Promise.all (Modern Approach)
Replace async.parallel entirely with native Promises:
Arrow functions and async/await eliminate the closure issue entirely because each item is a parameter of the map callback.
Fix 5: Use async/await with for...of
This runs queries sequentially (not in parallel), but each item is properly scoped. Use Promise.all with map for parallel execution.
Understanding var vs let Scoping
This same principle applies to async.parallel, MongoDB callbacks, and any other asynchronous operation inside a loop.
MongoDB Connection Considerations
Common Pitfalls
- Using
varin loops with async callbacks:varis function-scoped, so all callbacks share the same variable. Always useletorconstin loops containing async operations. - Mutating the array during iteration: If you modify
itemswhileasync.parallelis running, callbacks may see changed values. Create a copy with[...items]if the source array might change. - Opening new MongoDB connections per task: Each
async.paralleltask should reuse a shared connection pool, not callMongoClient.connect()individually. Connection overhead dominates small queries. - Missing error handling in callbacks: If one parallel task fails,
async.parallelcalls the final callback with the error immediately. Remaining in-flight tasks still complete but their results are ignored. - Using
async.parallelwhenPromise.allsuffices: Theasynclibrary was essential before native Promises. Modern Node.js (12+) supportsasync/awaitnatively — preferPromise.alloverasync.parallelin new code.
Summary
- Variables declared with
varare function-scoped and shared across loop iterations — async callbacks see the final value - Use
letorconstfor block scoping so each callback captures its own value Array.mapnaturally creates a new scope for each element- Modern Node.js should use
Promise.allwithasync/awaitinstead of theasynclibrary - Share MongoDB connections across parallel tasks using a connection pool

