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
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí