0

Understanding async/await pitfalls in JavaScript

Async/await makes asynchronous code look synchronous. But that simplicity hides pitfalls that can cause bugs, memory leaks, and performance issues.

Pitfall 1: Sequential When You Mean Parallel

// Slow: 2 seconds (sequential)
const users = await getUsers();
const orders = await getOrders();

// Fast: 1 second (parallel)
const [users, orders] = await Promise.all([
    getUsers(),
        getOrders()
        ]);
        ```
        
        If two operations don't depend on each other, run them in parallel.
        
        ## Pitfall 2: Unhandled Rejections
        
        ```javascript
        // Bad: if getUser throws, the error silently disappears
        async function loadDashboard() {
            getUser(123).then(u => updateUI(u));  // No await, no catch
            }
            
            // Good: handle the error
            async function loadDashboard() {
                try {
                        const user = await getUser(123);
                                updateUI(user);
                                    } catch (err) {
                                            showError(err);
                                                }
                                                }
                                                ```
                                                
                                                In Node.js, unhandled rejections crash the process (as of Node 15+).
                                                
                                                ## Pitfall 3: Async in forEach
                                                
                                                ```javascript
                                                // BROKEN: forEach doesn't await
                                                const ids = [1, 2, 3];
                                                ids.forEach(async (id) => {
                                                    await processItem(id);  // These run in parallel, not sequential
                                                    });
                                                    console.log('Done!');  // Runs before processing finishes
                                                    
                                                    // Fix: for...of for sequential
                                                    for (const id of ids) {
                                                        await processItem(id);
                                                        }
                                                        
                                                        // Fix: Promise.all for parallel
                                                        await Promise.all(ids.map(id => processItem(id)));
                                                        ```
                                                        
                                                        ## Pitfall 4: Async Constructor Trap
                                                        
                                                        ```javascript
                                                        // Can't use async constructor
                                                        class Database {
                                                            constructor() {
                                                                    // This doesn't work as expected
                                                                            this.connection = await connect();  // SyntaxError
                                                                                }
                                                                                }
                                                                                
                                                                                // Fix: factory function
                                                                                class Database {
                                                                                    static async create() {
                                                                                            const db = new Database();
                                                                                                    db.connection = await connect();
                                                                                                            return db;
                                                                                                                }
                                                                                                                }
                                                                                                                
                                                                                                                const db = await Database.create();
                                                                                                                ```
                                                                                                                
                                                                                                                ## Pitfall 5: Error Swallowing in Promise.all
                                                                                                                
                                                                                                                ```javascript
                                                                                                                // If one fails, all results are lost
                                                                                                                try {
                                                                                                                    const results = await Promise.all([
                                                                                                                            fetchUser(1),    // succeeds
                                                                                                                                    fetchUser(999),  // fails
                                                                                                                                            fetchUser(2),    // succeeds but result lost
                                                                                                                                                ]);
                                                                                                                                                } catch (err) {
                                                                                                                                                    // Only get the first error, lose all successful results
                                                                                                                                                    }
                                                                                                                                                    
                                                                                                                                                    // Fix: Promise.allSettled
                                                                                                                                                    const results = await Promise.allSettled([
                                                                                                                                                        fetchUser(1),
                                                                                                                                                            fetchUser(999),
                                                                                                                                                                fetchUser(2),
                                                                                                                                                                ]);
                                                                                                                                                                
                                                                                                                                                                results.forEach((result, i) => {
                                                                                                                                                                    if (result.status === 'fulfilled') {
                                                                                                                                                                            console.log(`User ${i}: ${result.value.name}`);
                                                                                                                                                                                } else {
                                                                                                                                                                                        console.log(`User ${i} failed: ${result.reason}`);
                                                                                                                                                                                            }
                                                                                                                                                                                            });
                                                                                                                                                                                            ```
                                                                                                                                                                                            
                                                                                                                                                                                            ## Pitfall 6: Awaiting Non-Promises
                                                                                                                                                                                            
                                                                                                                                                                                            ```javascript
                                                                                                                                                                                            // Works but unnecessary overhead
                                                                                                                                                                                            const x = await 42;  // Wraps in Promise.resolve(42)
                                                                                                                                                                                            const y = await someObj;  // Wraps non-thenable
                                                                                                                                                                                            
                                                                                                                                                                                            // This matters in loops
                                                                                                                                                                                            for (const item of items) {
                                                                                                                                                                                                const result = await syncFunction(item);  // Don't await sync functions
                                                                                                                                                                                                }
                                                                                                                                                                                                ```
                                                                                                                                                                                                
                                                                                                                                                                                                ## Pitfall 7: Missing Return in Async
                                                                                                                                                                                                
                                                                                                                                                                                                ```javascript
                                                                                                                                                                                                // Subtle bug: returns undefined, not the user
                                                                                                                                                                                                async function getUser(id) {
                                                                                                                                                                                                    const user = await db.findUser(id);
                                                                                                                                                                                                        if (!user) throw new NotFoundError();
                                                                                                                                                                                                            user;  // Oops, forgot 'return'
                                                                                                                                                                                                            }
                                                                                                                                                                                                            ```
                                                                                                                                                                                                            
                                                                                                                                                                                                            ## The async/await Checklist
                                                                                                                                                                                                            
                                                                                                                                                                                                            1. **Independent operations?** → `Promise.all`
                                                                                                                                                                                                            2. **Need all results even if some fail?** → `Promise.allSettled`
                                                                                                                                                                                                            3. **Iterating async?** → `for...of`, never `forEach`
                                                                                                                                                                                                            4. **Error handling?** → `try/catch` at boundaries
                                                                                                                                                                                                            5. **Cleanup needed?** → `try/finally`
                                                                                                                                                                                                            
                                                                                                                                                                                                            ---
                                                                                                                                                                                                            
                                                                                                                                                                                                            What async bug have you spent the most time debugging?

All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.