Close Menu
    DevStackTipsDevStackTips
    • Home
    • News & Updates
      1. Tech & Work
      2. View All

      Error’d: Pickup Sticklers

      September 27, 2025

      From Prompt To Partner: Designing Your Custom AI Assistant

      September 27, 2025

      Microsoft unveils reimagined Marketplace for cloud solutions, AI apps, and more

      September 27, 2025

      Design Dialects: Breaking the Rules, Not the System

      September 27, 2025

      Building personal apps with open source and AI

      September 12, 2025

      What Can We Actually Do With corner-shape?

      September 12, 2025

      Craft, Clarity, and Care: The Story and Work of Mengchu Yao

      September 12, 2025

      Cailabs secures €57M to accelerate growth and industrial scale-up

      September 12, 2025
    • Development
      1. Algorithms & Data Structures
      2. Artificial Intelligence
      3. Back-End Development
      4. Databases
      5. Front-End Development
      6. Libraries & Frameworks
      7. Machine Learning
      8. Security
      9. Software Engineering
      10. Tools & IDEs
      11. Web Design
      12. Web Development
      13. Web Security
      14. Programming Languages
        • PHP
        • JavaScript
      Featured

      Using phpinfo() to Debug Common and Not-so-Common PHP Errors and Warnings

      September 28, 2025
      Recent

      Using phpinfo() to Debug Common and Not-so-Common PHP Errors and Warnings

      September 28, 2025

      Mastering PHP File Uploads: A Guide to php.ini Settings and Code Examples

      September 28, 2025

      The first browser with JavaScript landed 30 years ago

      September 27, 2025
    • Operating Systems
      1. Windows
      2. Linux
      3. macOS
      Featured
      Recent
    • Learning Resources
      • Books
      • Cheatsheets
      • Tutorials & Guides
    Home»Development»When to Use Async/Await vs Promises in JavaScript

    When to Use Async/Await vs Promises in JavaScript

    July 1, 2025

    JavaScript is an asynchronous programming language, which means it can handle multiple operations at the same time without blocking the main thread. When working with asynchronous operations like API calls, file reading, or database queries, you have two main approaches: Promises and Async/Await.

    In this article, you will learn the differences between these two approaches, when to use each one, and how to make the right choice for your specific use case.

    Here’s what we’ll cover:

    1. What Are Asynchronous Operations?

    2. What Are Promises?

    3. What Is Async/Await?

    4. Practical Examples: Promises vs Async/Await

    5. When to Use Promises

    6. When to Use Async/Await

    7. Performance Considerations

    8. Error Handling Patterns

    9. Best Practices

    10. Making the Right Choice

    11. Conclusion

    What Are Asynchronous Operations?

    Before explaining what Promises and Async/Await mean, it is important to understand what asynchronous operations are.

    Synchronous operations execute one after another, blocking the next operation until the current one completes. Here’s an example in JavaScript:

    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"First"</span>);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Second"</span>);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Third"</span>);
    
    <span class="hljs-comment">// Output:</span>
    <span class="hljs-comment">// First</span>
    <span class="hljs-comment">// Second</span>
    <span class="hljs-comment">// Third</span>
    

    Asynchronous operations, on the other hand, can start an operation and continue executing other code while waiting for the first operation to complete. Here’s an example in JavaScript:

    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"First"</span>);
    
    <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =></span> {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Second (after 2 seconds)"</span>);
    }, <span class="hljs-number">2000</span>);
    
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Third"</span>);
    
    <span class="hljs-comment">// Output:</span>
    <span class="hljs-comment">// First</span>
    <span class="hljs-comment">// Third</span>
    <span class="hljs-comment">// Second (after 2 seconds)</span>
    

    In this example, setTimeout() is an asynchronous function that schedules code to run after a specified delay without blocking the execution of subsequent code.

    What Are Promises?

    A Promise is a JavaScript object that represents the eventual completion (or failure) of an asynchronous operation. Think of it as a placeholder for a value that will be available in the future.

    Promise States

    A Promise can be in one of three states:

    1. Pending: The initial state – the operation hasn’t been completed yet

    2. Fulfilled (Resolved): The operation completed successfully

    3. Rejected: The operation failed

    Basic Promise Syntax

    Here’s how you create and use a basic Promise:

    <span class="hljs-comment">// Creating a Promise</span>
    <span class="hljs-keyword">const</span> myPromise = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =></span> {
        <span class="hljs-comment">// Simulate an asynchronous operation</span>
        <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =></span> {
            <span class="hljs-keyword">const</span> success = <span class="hljs-literal">true</span>;
    
            <span class="hljs-keyword">if</span> (success) {
                resolve(<span class="hljs-string">"Operation completed successfully!"</span>);
            } <span class="hljs-keyword">else</span> {
                reject(<span class="hljs-string">"Operation failed!"</span>);
            }
        }, <span class="hljs-number">2000</span>);
    });
    
    <span class="hljs-comment">// Using the Promise</span>
    myPromise
        .then(<span class="hljs-function">(<span class="hljs-params">result</span>) =></span> {
            <span class="hljs-built_in">console</span>.log(result); <span class="hljs-comment">// "Operation completed successfully!"</span>
        })
        .catch(<span class="hljs-function">(<span class="hljs-params">error</span>) =></span> {
            <span class="hljs-built_in">console</span>.log(error);
        });
    

    Let’s break down this code:

    • new Promise() creates a new Promise object

    • The Promise constructor takes a function with two parameters: resolve and reject

    • resolve() is called when the operation succeeds

    • reject() is called when the operation fails

    • .then() handles the successful case

    • .catch() handles the error case

    Chaining Promises

    Promise chaining is a powerful technique that allows you to link multiple asynchronous operations together in a sequence. When you want to perform multiple operations where each depends on the result of the previous one, promise chaining provides an elegant solution. You can chain multiple Promises together using .then():

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchUserData</span>(<span class="hljs-params">userId</span>) </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =></span> {
            <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =></span> {
                resolve({ <span class="hljs-attr">id</span>: userId, <span class="hljs-attr">name</span>: <span class="hljs-string">"John Doe"</span> });
            }, <span class="hljs-number">1000</span>);
        });
    }
    
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchUserPosts</span>(<span class="hljs-params">user</span>) </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =></span> {
            <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =></span> {
                resolve([
                    { <span class="hljs-attr">title</span>: <span class="hljs-string">"Post 1"</span>, <span class="hljs-attr">author</span>: user.name },
                    { <span class="hljs-attr">title</span>: <span class="hljs-string">"Post 2"</span>, <span class="hljs-attr">author</span>: user.name }
                ]);
            }, <span class="hljs-number">1000</span>);
        });
    }
    
    <span class="hljs-comment">// Chaining Promises</span>
    fetchUserData(<span class="hljs-number">123</span>)
        .then(<span class="hljs-function">(<span class="hljs-params">user</span>) =></span> {
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"User:"</span>, user);
            <span class="hljs-keyword">return</span> fetchUserPosts(user);
        })
        .then(<span class="hljs-function">(<span class="hljs-params">posts</span>) =></span> {
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Posts:"</span>, posts);
        })
        .catch(<span class="hljs-function">(<span class="hljs-params">error</span>) =></span> {
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Error:"</span>, error);
        });
    

    In this example:

    • fetchUserData() returns a Promise that resolves with user information

    • fetchUserPosts() returns a Promise that resolves with the user’s posts

    • We chain these operations using .then()

    • Each .then() receives the resolved value from the previous Promise

    Downsides of Promise Chaining:

    While promise chaining is powerful, it does have some potential drawbacks:

    1. “Callback Hell” in disguise: Complex chains can become difficult to read and debug, especially with nested logic

    2. Complex error handling: Each step in the chain needs proper error handling, and errors can propagate in unexpected ways

    3. Debugging challenges: Stack traces through promise chains can be harder to follow

    4. Mixing synchronous and asynchronous logic: It can be tempting to put synchronous operations inside .then() blocks, which can lead to confusion

    What Is Async/Await?

    Async/Await is syntactic sugar built on top of Promises. It allows you to write asynchronous code that looks and behaves more like synchronous code, making it easier to read and understand.

    Basic Async/Await Syntax

    Here’s the same Promise example rewritten using Async/Await:

    <span class="hljs-comment">// Creating an async function</span>
    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">performOperation</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> myPromise;
            <span class="hljs-built_in">console</span>.log(result); <span class="hljs-comment">// "Operation completed successfully!"</span>
        } <span class="hljs-keyword">catch</span> (error) {
            <span class="hljs-built_in">console</span>.log(error);
        }
    }
    
    performOperation();
    

    Let’s break down this code:

    • The async keyword before a function declaration makes it an asynchronous function

    • The await keyword pauses the function execution until the Promise resolves

    • The try/catch blocks handle errors, similar to .catch() in Promises

    Converting Promise Chains to Async/Await

    Here’s the previous chaining example using Async/Await:

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getUserDataAndPosts</span>(<span class="hljs-params">userId</span>) </span>{
        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> fetchUserData(userId);
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"User:"</span>, user);
    
            <span class="hljs-keyword">const</span> posts = <span class="hljs-keyword">await</span> fetchUserPosts(user);
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Posts:"</span>, posts);
    
            <span class="hljs-keyword">return</span> posts;
        } <span class="hljs-keyword">catch</span> (error) {
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Error:"</span>, error);
            <span class="hljs-keyword">throw</span> error; <span class="hljs-comment">// Re-throw the error if needed</span>
        }
    }
    
    getUserDataAndPosts(<span class="hljs-number">123</span>);
    

    This code is much more readable and follows a linear flow that’s easier to understand.

    Practical Examples: Promises vs Async/Await

    Let’s compare both approaches with real-world scenarios.

    Example 1: Making API Calls

    Using Promises:

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchDataWithPromises</span>(<span class="hljs-params"></span>) </span>{
        fetch(<span class="hljs-string">'https://jsonplaceholder.typicode.com/users/1'</span>)
            .then(<span class="hljs-function"><span class="hljs-params">response</span> =></span> {
                <span class="hljs-keyword">if</span> (!response.ok) {
                    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Network response was not ok'</span>);
                }
                <span class="hljs-keyword">return</span> response.json();
            })
            .then(<span class="hljs-function"><span class="hljs-params">user</span> =></span> {
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'User data:'</span>, user);
                <span class="hljs-keyword">return</span> fetch(<span class="hljs-string">`https://jsonplaceholder.typicode.com/users/<span class="hljs-subst">${user.id}</span>/posts`</span>);
            })
            .then(<span class="hljs-function"><span class="hljs-params">response</span> =></span> response.json())
            .then(<span class="hljs-function"><span class="hljs-params">posts</span> =></span> {
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'User posts:'</span>, posts);
            })
            .catch(<span class="hljs-function"><span class="hljs-params">error</span> =></span> {
                <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error:'</span>, error);
            });
    }
    

    Using Async/Await:

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchDataWithAsyncAwait</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">const</span> userResponse = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'https://jsonplaceholder.typicode.com/users/1'</span>);
    
            <span class="hljs-keyword">if</span> (!userResponse.ok) {
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Network response was not ok'</span>);
            }
    
            <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> userResponse.json();
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'User data:'</span>, user);
    
            <span class="hljs-keyword">const</span> postsResponse = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">`https://jsonplaceholder.typicode.com/users/<span class="hljs-subst">${user.id}</span>/posts`</span>);
            <span class="hljs-keyword">const</span> posts = <span class="hljs-keyword">await</span> postsResponse.json();
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'User posts:'</span>, posts);
    
        } <span class="hljs-keyword">catch</span> (error) {
            <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error:'</span>, error);
        }
    }
    

    The Async/Await version is more readable and follows a natural top-to-bottom flow.

    Example 2: Handling Multiple Asynchronous Operations

    Using Promises:

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">processMultipleOperations</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">const</span> promise1 = fetch(<span class="hljs-string">'https://jsonplaceholder.typicode.com/users/1'</span>);
        <span class="hljs-keyword">const</span> promise2 = fetch(<span class="hljs-string">'https://jsonplaceholder.typicode.com/users/2'</span>);
        <span class="hljs-keyword">const</span> promise3 = fetch(<span class="hljs-string">'https://jsonplaceholder.typicode.com/users/3'</span>);
    
        <span class="hljs-built_in">Promise</span>.all([promise1, promise2, promise3])
            .then(<span class="hljs-function"><span class="hljs-params">responses</span> =></span> {
                <span class="hljs-keyword">return</span> <span class="hljs-built_in">Promise</span>.all(responses.map(<span class="hljs-function"><span class="hljs-params">response</span> =></span> response.json()));
            })
            .then(<span class="hljs-function"><span class="hljs-params">users</span> =></span> {
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'All users:'</span>, users);
            })
            .catch(<span class="hljs-function"><span class="hljs-params">error</span> =></span> {
                <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error:'</span>, error);
            });
    }
    

    Using Async/Await:

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">processMultipleOperationsAsync</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">const</span> promise1 = fetch(<span class="hljs-string">'https://jsonplaceholder.typicode.com/users/1'</span>);
            <span class="hljs-keyword">const</span> promise2 = fetch(<span class="hljs-string">'https://jsonplaceholder.typicode.com/users/2'</span>);
            <span class="hljs-keyword">const</span> promise3 = fetch(<span class="hljs-string">'https://jsonplaceholder.typicode.com/users/3'</span>);
    
            <span class="hljs-keyword">const</span> responses = <span class="hljs-keyword">await</span> <span class="hljs-built_in">Promise</span>.all([promise1, promise2, promise3]);
            <span class="hljs-keyword">const</span> users = <span class="hljs-keyword">await</span> <span class="hljs-built_in">Promise</span>.all(responses.map(<span class="hljs-function"><span class="hljs-params">response</span> =></span> response.json()));
    
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'All users:'</span>, users);
        } <span class="hljs-keyword">catch</span> (error) {
            <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error:'</span>, error);
        }
    }
    

    Both approaches use Promise. all() to wait for multiple operations to complete simultaneously.

    When to Use Promises

    Promises are still useful in several scenarios:

    1. Working with Existing Promise-Based APIs

    Popular libraries like Axios, fetch(), and many Node.js modules return Promises.

    How to identify promise-based APIs:

    • The function returns an object with .then() and .catch() methods

    • The documentation mentions “returns a Promise”

    • The function doesn’t require a callback parameter

    Many libraries and APIs return Promises directly:

    <span class="hljs-comment">// Axios library returns Promises</span>
    axios.get(<span class="hljs-string">'/api/users'</span>)
        .then(<span class="hljs-function"><span class="hljs-params">response</span> =></span> response.data)
        .then(<span class="hljs-function"><span class="hljs-params">users</span> =></span> <span class="hljs-built_in">console</span>.log(users))
        .catch(<span class="hljs-function"><span class="hljs-params">error</span> =></span> <span class="hljs-built_in">console</span>.error(error));
    
    <span class="hljs-comment">// fetch() API returns Promises</span>
    fetch(<span class="hljs-string">'/api/data'</span>)
        .then(<span class="hljs-function"><span class="hljs-params">response</span> =></span> response.json())
        .then(<span class="hljs-function"><span class="hljs-params">data</span> =></span> <span class="hljs-built_in">console</span>.log(data));
    
    <span class="hljs-comment">// Node.js fs.promises returns Promises</span>
    <span class="hljs-keyword">import</span> { readFile } <span class="hljs-keyword">from</span> <span class="hljs-string">'fs/promises'</span>;
    readFile(<span class="hljs-string">'./config.json'</span>, <span class="hljs-string">'utf8'</span>)
        .then(<span class="hljs-function"><span class="hljs-params">data</span> =></span> <span class="hljs-built_in">JSON</span>.parse(data))
        .then(<span class="hljs-function"><span class="hljs-params">config</span> =></span> <span class="hljs-built_in">console</span>.log(config));
    

    2. Functional Programming Patterns

    Promises are immutable objects that represent future values, making them perfect for functional programming approaches. They can be easily composed, chained, and transformed without side effects. The .then() method essentially maps over the future value, similar to how Array.map() works with collections.

    Promises work well with functional programming approaches because they are composable and can be easily passed around as first-class objects:

    <span class="hljs-comment">// Functional composition with Promises</span>
    <span class="hljs-keyword">const</span> processUsers = <span class="hljs-function">(<span class="hljs-params">userIds</span>) =></span> {
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">Promise</span>.all(
            userIds.map(<span class="hljs-function"><span class="hljs-params">id</span> =></span> fetchUser(id))  <span class="hljs-comment">// Transform each ID to a Promise</span>
        )
        .then(<span class="hljs-function"><span class="hljs-params">users</span> =></span> users.filter(<span class="hljs-function"><span class="hljs-params">user</span> =></span> user.active))  <span class="hljs-comment">// Filter active users</span>
        .then(<span class="hljs-function"><span class="hljs-params">activeUsers</span> =></span> activeUsers.map(<span class="hljs-function"><span class="hljs-params">user</span> =></span> user.email));  <span class="hljs-comment">// Extract emails</span>
    };
    
    <span class="hljs-comment">// Pipeline approach</span>
    <span class="hljs-keyword">const</span> createUserPipeline = <span class="hljs-function">(<span class="hljs-params">userId</span>) =></span> {
        <span class="hljs-keyword">return</span> fetchUser(userId)
            .then(validateUser)
            .then(enrichUserData)
            .then(formatUserResponse)
            .then(logUserActivity);
    };
    
    <span class="hljs-comment">// Composing multiple Promise-returning functions</span>
    <span class="hljs-keyword">const</span> compose = <span class="hljs-function">(<span class="hljs-params">...fns</span>) =></span> <span class="hljs-function">(<span class="hljs-params">value</span>) =></span> 
        fns.reduce(<span class="hljs-function">(<span class="hljs-params">promise, fn</span>) =></span> promise.then(fn), <span class="hljs-built_in">Promise</span>.resolve(value));
    
    <span class="hljs-keyword">const</span> userProcessor = compose(
        fetchUser,
        validateUser,
        enrichUserData,
        saveUser
    );
    

    3. Creating Reusable Promise Utilities

    Reusable promise utilities are helper functions that abstract common asynchronous patterns into reusable components. They’re particularly useful for cross-cutting concerns like retries, timeouts, rate limiting, and caching. These utilities can be used across different parts of your application without being tied to specific business logic.

    When they’re useful:

    • When you need the same asynchronous pattern in multiple places

    • For handling common failure scenarios (network timeouts, retries)

    • When building middleware or interceptors

    • For performance optimizations like batching or debouncing

    <span class="hljs-comment">// Timeout utility</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">timeout</span>(<span class="hljs-params">ms</span>) </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function"><span class="hljs-params">resolve</span> =></span> <span class="hljs-built_in">setTimeout</span>(resolve, ms));
    }
    
    <span class="hljs-comment">// Retry utility with exponential backoff</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">retry</span>(<span class="hljs-params">fn, retries = <span class="hljs-number">3</span>, delay = <span class="hljs-number">1000</span></span>) </span>{
        <span class="hljs-keyword">return</span> fn().catch(<span class="hljs-function"><span class="hljs-params">error</span> =></span> {
            <span class="hljs-keyword">if</span> (retries > <span class="hljs-number">0</span>) {
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Retrying... <span class="hljs-subst">${retries}</span> attempts left`</span>);
                <span class="hljs-keyword">return</span> timeout(delay).then(<span class="hljs-function">() =></span> retry(fn, retries - <span class="hljs-number">1</span>, delay * <span class="hljs-number">2</span>));
            }
            <span class="hljs-keyword">throw</span> error;
        });
    }
    
    <span class="hljs-comment">// Rate limiting utility</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">rateLimit</span>(<span class="hljs-params">fn, maxCalls, timeWindow</span>) </span>{
        <span class="hljs-keyword">let</span> calls = [];
    
        <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">...args</span>) </span>{
            <span class="hljs-keyword">const</span> now = <span class="hljs-built_in">Date</span>.now();
            calls = calls.filter(<span class="hljs-function"><span class="hljs-params">time</span> =></span> now - time < timeWindow);
    
            <span class="hljs-keyword">if</span> (calls.length >= maxCalls) {
                <span class="hljs-keyword">const</span> waitTime = timeWindow - (now - calls[<span class="hljs-number">0</span>]);
                <span class="hljs-keyword">return</span> timeout(waitTime).then(<span class="hljs-function">() =></span> fn.apply(<span class="hljs-built_in">this</span>, args));
            }
    
            calls.push(now);
            <span class="hljs-keyword">return</span> fn.apply(<span class="hljs-built_in">this</span>, args);
        };
    }
    
    <span class="hljs-comment">// Usage examples</span>
    <span class="hljs-keyword">const</span> apiCall = <span class="hljs-function">() =></span> fetch(<span class="hljs-string">'/api/data'</span>).then(<span class="hljs-function"><span class="hljs-params">r</span> =></span> r.json());
    <span class="hljs-keyword">const</span> resilientApiCall = retry(apiCall, <span class="hljs-number">3</span>);
    <span class="hljs-keyword">const</span> rateLimitedApiCall = rateLimit(apiCall, <span class="hljs-number">5</span>, <span class="hljs-number">60000</span>); <span class="hljs-comment">// 5 calls per minute</span>
    

    When to Use Async/Await

    Async/Await is preferred in most modern JavaScript applications. It has various advantages over Promises, such as:

    1. Improved readability: Code reads like synchronous code, making it easier to understand the flow

    2. Better debugging: Stack traces are cleaner and easier to follow

    3. Simplified error handling: Single try/catch block can handle multiple async operations

    4. Reduced nesting: Eliminates the “pyramid of doom” that can occur with promise chains

    5. Easier testing: Async functions are easier to test and mock

    6. Better IDE support: Better autocomplete and type inference in modern editors

    Let’s look at some examples that demonstrate when async/await would be a better choice.

    1. Sequential Operations

    When you need to perform operations one after another:

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">processUserData</span>(<span class="hljs-params">userId</span>) </span>{
        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> fetchUser(userId);
            <span class="hljs-keyword">const</span> preferences = <span class="hljs-keyword">await</span> fetchUserPreferences(user.id);
            <span class="hljs-keyword">const</span> recommendations = <span class="hljs-keyword">await</span> generateRecommendations(user, preferences);
    
            <span class="hljs-keyword">return</span> {
                user,
                preferences,
                recommendations
            };
        } <span class="hljs-keyword">catch</span> (error) {
            <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Failed to process user data:'</span>, error);
            <span class="hljs-keyword">throw</span> error;
        }
    }
    

    Why this is better than promises: With promise chaining, you’d need to nest .then() calls or return values through the chain, making it harder to track data flow.

    2. Complex Error Handling

    Async/await allows you to use familiar try/catch syntax and handle errors at the exact point where they might occur. You can have multiple try/catch blocks for different error scenarios, and the error handling logic is co-located with the code that might throw the error.

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">complexOperation</span>(<span class="hljs-params">data</span>) </span>{
        <span class="hljs-keyword">try</span> {
            <span class="hljs-comment">// First level: preprocessing errors</span>
            <span class="hljs-keyword">const</span> processedData = <span class="hljs-keyword">await</span> preprocessData(data);
    
            <span class="hljs-keyword">try</span> {
                <span class="hljs-comment">// Second level: critical operation errors</span>
                <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> performCriticalOperation(processedData);
                <span class="hljs-keyword">return</span> result;
            } <span class="hljs-keyword">catch</span> (criticalError) {
                <span class="hljs-comment">// Handle critical operation errors specifically</span>
                <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Critical operation failed:'</span>, criticalError);
    
                <span class="hljs-comment">// We can make decisions based on the error type</span>
                <span class="hljs-keyword">if</span> (criticalError.code === <span class="hljs-string">'TEMPORARY_FAILURE'</span>) {
                    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Attempting fallback operation...'</span>);
                    <span class="hljs-keyword">const</span> fallbackResult = <span class="hljs-keyword">await</span> performFallbackOperation(processedData);
                    <span class="hljs-keyword">return</span> fallbackResult;
                } <span class="hljs-keyword">else</span> {
                    <span class="hljs-comment">// Re-throw if it's not recoverable</span>
                    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`Critical failure: <span class="hljs-subst">${criticalError.message}</span>`</span>);
                }
            }
    
        } <span class="hljs-keyword">catch</span> (preprocessError) {
            <span class="hljs-comment">// Handle preprocessing errors differently</span>
            <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Preprocessing failed:'</span>, preprocessError);
    
            <span class="hljs-comment">// We can inspect the error and decide how to handle it</span>
            <span class="hljs-keyword">if</span> (preprocessError.code === <span class="hljs-string">'INVALID_DATA'</span>) {
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Invalid input data provided'</span>);
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Unable to process data'</span>);
            }
        }
    }
    

    Explanation of the code:

    • The outer try/catch handles preprocessing errors

    • The inner try/catch specifically handles critical operation errors

    • Each error handler can make different decisions based on error types

    • The code clearly shows the error-handling strategy at each level

    • You can easily add logging, metrics, or recovery logic at each level

    3. Conditional Asynchronous Logic

    Async/await makes it natural to use standard control flow (if/else, loops, switch statements) with asynchronous operations. This is much cleaner than trying to implement conditional logic within promise chains.

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">smartUserProcess</span>(<span class="hljs-params">userId</span>) </span>{
        <span class="hljs-keyword">try</span> {
            <span class="hljs-comment">// First, get the user data</span>
            <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> fetchUser(userId);
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Processing user: <span class="hljs-subst">${user.name}</span> (Premium: <span class="hljs-subst">${user.isPremium}</span>)`</span>);
    
            <span class="hljs-comment">// Make decisions based on the async result</span>
            <span class="hljs-keyword">if</span> (user.isPremium) {
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'User is premium - fetching premium features'</span>);
    
                <span class="hljs-comment">// Premium users get additional data</span>
                <span class="hljs-keyword">const</span> premiumData = <span class="hljs-keyword">await</span> fetchPremiumFeatures(user.id);
    
                <span class="hljs-comment">// We can make further decisions based on premium data</span>
                <span class="hljs-keyword">if</span> (premiumData.analyticsEnabled) {
                    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Analytics enabled - generating premium analytics'</span>);
                    <span class="hljs-keyword">const</span> analytics = <span class="hljs-keyword">await</span> generatePremiumAnalytics(user, premiumData);
                    <span class="hljs-keyword">return</span> { user, premiumData, analytics };
                } <span class="hljs-keyword">else</span> {
                    <span class="hljs-keyword">return</span> { user, premiumData };
                }
    
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'User is basic - fetching basic features'</span>);
    
                <span class="hljs-comment">// Basic users get different treatment</span>
                <span class="hljs-keyword">const</span> basicData = <span class="hljs-keyword">await</span> fetchBasicFeatures(user.id);
    
                <span class="hljs-comment">// Check if user qualifies for upgrade prompts</span>
                <span class="hljs-keyword">if</span> (basicData.usageLevel > <span class="hljs-number">0.8</span>) {
                    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'User has high usage - checking upgrade eligibility'</span>);
                    <span class="hljs-keyword">const</span> upgradeOffer = <span class="hljs-keyword">await</span> checkUpgradeEligibility(user);
                    <span class="hljs-keyword">return</span> { user, basicData, upgradeOffer };
                } <span class="hljs-keyword">else</span> {
                    <span class="hljs-keyword">return</span> { user, basicData };
                }
            }
        } <span class="hljs-keyword">catch</span> (error) {
            <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'User processing failed:'</span>, error);
    
            <span class="hljs-comment">// Even error handling can be conditional</span>
            <span class="hljs-keyword">if</span> (error.code === <span class="hljs-string">'USER_NOT_FOUND'</span>) {
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'User does not exist'</span>);
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (error.code === <span class="hljs-string">'NETWORK_ERROR'</span>) {
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Network connectivity issue'</span>);
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-keyword">throw</span> error;
            }
        }
    }
    

    Explanation of the code:

    • We await the user fetch and immediately use the result in an if statement

    • Each branch of the conditional can perform different async operations

    • We can nest conditions naturally (like checking analyticsEnabled)

    • Standard control flow works seamlessly with async operations

    • Error handling can also be conditional based on error types

    • The code reads like synchronous code but handles async operations correctly

    Performance Considerations

    Understanding the performance implications of different asynchronous patterns is crucial for building efficient JavaScript applications. The main performance consideration is whether your asynchronous operations can run in parallel or must be executed sequentially.

    When working with multiple asynchronous operations, you have two main execution patterns: sequential (one after another) and parallel (multiple operations at the same time). The choice between these patterns can significantly impact your application’s performance and user experience.

    Sequential vs Parallel Execution

    Sequential Execution (slower but sometimes necessary):

    Sequential execution means waiting for each operation to complete before starting the next one. This is slower but necessary when operations depend on each other.

    <span class="hljs-comment">// This takes ~3 seconds total (1 + 1 + 1)</span>
    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sequentialOperations</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-built_in">console</span>.time(<span class="hljs-string">'Sequential Operations'</span>);
    
        <span class="hljs-keyword">const</span> result1 = <span class="hljs-keyword">await</span> operation1(); <span class="hljs-comment">// 1 second - must complete first</span>
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Operation 1 completed:'</span>, result1);
    
        <span class="hljs-keyword">const</span> result2 = <span class="hljs-keyword">await</span> operation2(); <span class="hljs-comment">// 1 second - starts after operation1</span>
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Operation 2 completed:'</span>, result2);
    
        <span class="hljs-keyword">const</span> result3 = <span class="hljs-keyword">await</span> operation3(); <span class="hljs-comment">// 1 second - starts after operation2</span>
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Operation 3 completed:'</span>, result3);
    
        <span class="hljs-built_in">console</span>.timeEnd(<span class="hljs-string">'Sequential Operations'</span>);
        <span class="hljs-keyword">return</span> [result1, result2, result3];
    }
    

    Use sequential execution when:

    • Each operation depends on the result of the previous one

    • You need to process results in a specific order

    • Operations must be rate-limited (for example, API calls with rate limits)

    • You want to avoid overwhelming external services

    Parallel Execution (faster when possible):

    Parallel execution means starting all operations at the same time and waiting for all of them to complete. This is much faster when operations are independent.

    <span class="hljs-comment">// This takes ~1 second total (all operations run simultaneously)</span>
    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">parallelOperations</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-built_in">console</span>.time(<span class="hljs-string">'Parallel Operations'</span>);
    
        <span class="hljs-comment">// Start all operations immediately - they run concurrently</span>
        <span class="hljs-keyword">const</span> promise1 = operation1(); <span class="hljs-comment">// starts immediately</span>
        <span class="hljs-keyword">const</span> promise2 = operation2(); <span class="hljs-comment">// starts immediately  </span>
        <span class="hljs-keyword">const</span> promise3 = operation3(); <span class="hljs-comment">// starts immediately</span>
    
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'All operations started, waiting for completion...'</span>);
    
        <span class="hljs-comment">// Wait for all operations to complete</span>
        <span class="hljs-keyword">const</span> [result1, result2, result3] = <span class="hljs-keyword">await</span> <span class="hljs-built_in">Promise</span>.all([
            promise1,
            promise2,
            promise3
        ]);
    
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'All operations completed'</span>);
        <span class="hljs-built_in">console</span>.timeEnd(<span class="hljs-string">'Parallel Operations'</span>);
        <span class="hljs-keyword">return</span> [result1, result2, result3];
    }
    

    Use parallel execution when:

    • Operations are independent of each other

    • You want to minimize total execution time

    • Dealing with I/O operations (file reads, API calls, database queries)

    • The order of completion doesn’t matter

    Advanced Example – Mixed Approach:

    Sometimes you need a combination of both approaches:

    javascriptasync <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mixedApproach</span>(<span class="hljs-params">userIds</span>) </span>{
        <span class="hljs-built_in">console</span>.time(<span class="hljs-string">'Mixed Approach'</span>);
    
        <span class="hljs-comment">// Step 1: Fetch all users in parallel (they're independent)</span>
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Fetching users in parallel...'</span>);
        <span class="hljs-keyword">const</span> users = <span class="hljs-keyword">await</span> <span class="hljs-built_in">Promise</span>.all(
            userIds.map(<span class="hljs-function"><span class="hljs-params">id</span> =></span> fetchUser(id))
        );
    
        <span class="hljs-comment">// Step 2: Process each user sequentially (to avoid overwhelming the recommendation service)</span>
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Processing users sequentially...'</span>);
        <span class="hljs-keyword">const</span> results = [];
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> user <span class="hljs-keyword">of</span> users) {
            <span class="hljs-keyword">const</span> preferences = <span class="hljs-keyword">await</span> fetchUserPreferences(user.id);
            <span class="hljs-keyword">const</span> recommendations = <span class="hljs-keyword">await</span> generateRecommendations(user, preferences);
            results.push({ user, preferences, recommendations });
    
            <span class="hljs-comment">// Small delay to be respectful to the API</span>
            <span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function"><span class="hljs-params">resolve</span> =></span> <span class="hljs-built_in">setTimeout</span>(resolve, <span class="hljs-number">100</span>));
        }
    
        <span class="hljs-built_in">console</span>.timeEnd(<span class="hljs-string">'Mixed Approach'</span>);
        <span class="hljs-keyword">return</span> results;
    }
    

    Use Promise.all() when operations can run independently and simultaneously.

    Error Handling Patterns

    Proper error handling is crucial for building robust applications. Different asynchronous patterns offer different approaches to error handling, each with their own advantages and use cases.

    Error handling in asynchronous JavaScript can be challenging because errors can occur at different points in the execution flow. Understanding how to properly catch, handle, and recover from errors in both Promise and async/await patterns is essential for building reliable applications.

    Promise Error Handling:

    With Promises, errors are handled using the .catch() method. This approach provides fine-grained control over error handling at different points in the chain.

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">promiseErrorHandling</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">return</span> fetchData()
            .then(<span class="hljs-function"><span class="hljs-params">data</span> =></span> {
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Data fetched successfully'</span>);
                <span class="hljs-keyword">return</span> processData(data);
            })
            .then(<span class="hljs-function"><span class="hljs-params">result</span> =></span> {
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Data processed successfully'</span>);
                <span class="hljs-keyword">return</span> saveResult(result);
            })
            .then(<span class="hljs-function"><span class="hljs-params">savedResult</span> =></span> {
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Result saved successfully'</span>);
                <span class="hljs-keyword">return</span> savedResult;
            })
            .catch(<span class="hljs-function"><span class="hljs-params">error</span> =></span> {
                <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error occurred in the chain:'</span>, error);
    
                <span class="hljs-comment">// Handle different types of errors</span>
                <span class="hljs-keyword">if</span> (error.name === <span class="hljs-string">'NetworkError'</span>) {
                    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Network issue detected, attempting retry...'</span>);
                    <span class="hljs-keyword">return</span> retryOperation();
                } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (error.name === <span class="hljs-string">'ValidationError'</span>) {
                    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Data validation failed:'</span>, error.message);
                    <span class="hljs-keyword">return</span> handleValidationError(error);
                } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (error.name === <span class="hljs-string">'StorageError'</span>) {
                    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Storage operation failed, using fallback'</span>);
                    <span class="hljs-keyword">return</span> saveToFallbackStorage(error.data);
                } <span class="hljs-keyword">else</span> {
                    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Unknown error type:'</span>, error);
                    <span class="hljs-keyword">throw</span> error; <span class="hljs-comment">// Re-throw if we can't handle it</span>
                }
            });
    }
    

    Here’s what’s going on in this code:

    • The .catch() method catches any error that occurs in the entire chain

    • You can inspect the error object to determine the appropriate response

    • Returning a value from .catch() recovers from the error

    • Throwing an error or not returning anything propagates the error further

    • The error handler has access to the original error context

    Async/Await Error Handling

    With async/await, errors are handled using try/catch blocks, which provide more familiar and flexible error-handling patterns.

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">asyncAwaitErrorHandling</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">try</span> {
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Starting data processing...'</span>);
    
            <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> fetchData();
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Data fetched successfully'</span>);
    
            <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> processData(data);
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Data processed successfully'</span>);
    
            <span class="hljs-keyword">const</span> savedResult = <span class="hljs-keyword">await</span> saveResult(result);
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Result saved successfully'</span>);
    
            <span class="hljs-keyword">return</span> savedResult;
    
        } <span class="hljs-keyword">catch</span> (error) {
            <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error occurred during processing:'</span>, error);
    
            <span class="hljs-comment">// Handle different types of errors with more complex logic</span>
            <span class="hljs-keyword">if</span> (error.name === <span class="hljs-string">'NetworkError'</span>) {
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Network issue detected'</span>);
    
                <span class="hljs-comment">// We can use async operations in error handling</span>
                <span class="hljs-keyword">const</span> retryCount = <span class="hljs-keyword">await</span> getRetryCount();
                <span class="hljs-keyword">if</span> (retryCount < <span class="hljs-number">3</span>) {
                    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Retrying... (attempt <span class="hljs-subst">${retryCount + <span class="hljs-number">1</span>}</span>)`</span>);
                    <span class="hljs-keyword">await</span> incrementRetryCount();
                    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> retryOperation();
                } <span class="hljs-keyword">else</span> {
                    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Max retries reached, switching to offline mode'</span>);
                    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> switchToOfflineMode();
                }
    
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (error.name === <span class="hljs-string">'ValidationError'</span>) {
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Data validation failed:'</span>, error.message);
    
                <span class="hljs-comment">// Handle validation errors with user feedback</span>
                <span class="hljs-keyword">await</span> logValidationError(error);
                <span class="hljs-keyword">return</span> handleValidationError(error);
    
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (error.name === <span class="hljs-string">'StorageError'</span>) {
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Storage operation failed'</span>);
    
                <span class="hljs-comment">// Try multiple fallback options</span>
                <span class="hljs-keyword">try</span> {
                    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> saveToFallbackStorage(error.data);
                } <span class="hljs-keyword">catch</span> (fallbackError) {
                    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Fallback storage also failed, using cache'</span>);
                    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> saveToCache(error.data);
                }
    
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Unknown error type, logging for analysis'</span>);
                <span class="hljs-keyword">await</span> logErrorForAnalysis(error);
                <span class="hljs-keyword">throw</span> error; <span class="hljs-comment">// Re-throw unknown errors</span>
            }
        }
    }
    

    Here’s what’s going on in the code:

    • The try/catch block handles all errors in the async function

    • You can use await inside catch blocks for error recovery operations

    • Multiple nested try/catch blocks can handle different scenarios

    • Error handling code can be as complex as needed, including loops and conditions

    • The error handling logic is co-located with the code that might throw

    Advanced Error Pattern – Specific Error Handling:

    javascriptasync <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">advancedErrorHandling</span>(<span class="hljs-params">userId</span>) </span>{
        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> fetchUser(userId);
    
            <span class="hljs-keyword">try</span> {
                <span class="hljs-keyword">const</span> sensitiveData = <span class="hljs-keyword">await</span> fetchSensitiveData(user.id);
                <span class="hljs-keyword">return</span> { user, sensitiveData };
            } <span class="hljs-keyword">catch</span> (sensitiveError) {
                <span class="hljs-comment">// Handle sensitive data errors specifically</span>
                <span class="hljs-keyword">if</span> (sensitiveError.code === <span class="hljs-string">'PERMISSION_DENIED'</span>) {
                    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'User lacks permission for sensitive data'</span>);
                    <span class="hljs-keyword">return</span> { user, <span class="hljs-attr">sensitiveData</span>: <span class="hljs-literal">null</span>, <span class="hljs-attr">reason</span>: <span class="hljs-string">'permission_denied'</span> };
                } <span class="hljs-keyword">else</span> {
                    <span class="hljs-comment">// For other sensitive data errors, we still want to return the user</span>
                    <span class="hljs-built_in">console</span>.warn(<span class="hljs-string">'Sensitive data unavailable:'</span>, sensitiveError.message);
                    <span class="hljs-keyword">return</span> { user, <span class="hljs-attr">sensitiveData</span>: <span class="hljs-literal">null</span>, <span class="hljs-attr">reason</span>: <span class="hljs-string">'data_unavailable'</span> };
                }
            }
    
        } <span class="hljs-keyword">catch</span> (userError) {
            <span class="hljs-comment">// Handle user fetching errors</span>
            <span class="hljs-keyword">if</span> (userError.code === <span class="hljs-string">'USER_NOT_FOUND'</span>) {
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`User <span class="hljs-subst">${userId}</span> does not exist`</span>);
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (userError.code === <span class="hljs-string">'NETWORK_ERROR'</span>) {
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Unable to connect to user service'</span>);
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`Failed to fetch user: <span class="hljs-subst">${userError.message}</span>`</span>);
            }
        }
    }
    

    Best Practices

    Following these best practices will help you write more reliable, maintainable, and performant asynchronous JavaScript code.

    Always Handle Errors

    One of the most common mistakes in asynchronous JavaScript is forgetting to handle errors. When an async operation fails without proper error handling, it can lead to unhandled promise rejections, application crashes, or silent failures that are difficult to debug.

    Don’t do this:

    javascript<span class="hljs-comment">// Missing error handling - this is dangerous!</span>
    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">badExample</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> fetchData(); <span class="hljs-comment">// What if this fails?</span>
        <span class="hljs-keyword">const</span> processed = <span class="hljs-keyword">await</span> processData(data); <span class="hljs-comment">// What if this fails?</span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> saveData(processed); <span class="hljs-comment">// What if this fails?</span>
    }
    
    <span class="hljs-comment">// Usage - user has no idea if something went wrong</span>
    <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> badExample();
    <span class="hljs-built_in">console</span>.log(result); <span class="hljs-comment">// Could be undefined or cause a crash</span>
    

    Why this is problematic:

    • If fetchData() fails, the entire function crashes

    • The caller has no way to know what went wrong

    • In production, this could lead to a poor user experience

    • Debugging becomes much harder without proper error context

    Do this instead:

    javascript<span class="hljs-comment">// Proper error handling with context and recovery</span>
    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">goodExample</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">try</span> {
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Starting data processing...'</span>);
    
            <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> fetchData();
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Data fetched successfully'</span>);
    
            <span class="hljs-keyword">const</span> processed = <span class="hljs-keyword">await</span> processData(data);
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Data processed successfully'</span>);
    
            <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> saveData(processed);
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Data saved successfully'</span>);
    
            <span class="hljs-keyword">return</span> result;
        } <span class="hljs-keyword">catch</span> (error) {
            <span class="hljs-comment">// Log the error with context</span>
            <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Data processing failed:'</span>, {
                <span class="hljs-attr">error</span>: error.message,
                <span class="hljs-attr">step</span>: error.step || <span class="hljs-string">'unknown'</span>,
                <span class="hljs-attr">timestamp</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toISOString()
            });
    
            <span class="hljs-comment">// Re-throw with more context or handle appropriately</span>
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`Data processing failed: <span class="hljs-subst">${error.message}</span>`</span>);
        }
    }
    
    <span class="hljs-comment">// Usage - caller knows how to handle failures</span>
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> goodExample();
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Success:'</span>, result);
    } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Failed to process data:'</span>, error.message);
        <span class="hljs-comment">// Handle the error appropriately for your application</span>
        showErrorToUser(<span class="hljs-string">'Data processing failed. Please try again.'</span>);
    }
    

    Why this is better:

    • Every async operation is wrapped in try/catch

    • Errors are logged with useful context

    • The caller receives meaningful error messages

    • The application can gracefully handle failures

    Use Promise.all() for Independent Operations

    A common performance anti-pattern is making independent async operations run sequentially when they could run in parallel. This unnecessarily increases the total execution time.

    Don’t do this:

    javascript<span class="hljs-comment">// Sequential when it could be parallel - this is inefficient!</span>
    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">inefficient</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-built_in">console</span>.time(<span class="hljs-string">'Inefficient Approach'</span>);
    
        <span class="hljs-comment">// These operations are independent but run one after another</span>
        <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> fetchUser();        <span class="hljs-comment">// 500ms</span>
        <span class="hljs-keyword">const</span> posts = <span class="hljs-keyword">await</span> fetchPosts();      <span class="hljs-comment">// 300ms  </span>
        <span class="hljs-keyword">const</span> comments = <span class="hljs-keyword">await</span> fetchComments(); <span class="hljs-comment">// 400ms</span>
        <span class="hljs-comment">// Total time: ~1200ms</span>
    
        <span class="hljs-built_in">console</span>.timeEnd(<span class="hljs-string">'Inefficient Approach'</span>);
        <span class="hljs-keyword">return</span> { user, posts, comments };
    }
    

    Why this is problematic:

    • Each operation waits for the previous one to complete

    • Total execution time is the sum of all individual times

    • Network resources are underutilized

    • Users experience unnecessary delays

    This is particularly common when fetching data for a dashboard or page that needs multiple pieces of information. Developers often write the code sequentially without considering that these operations can run simultaneously.

    Do this instead:

    javascript<span class="hljs-comment">// Parallel execution - much more efficient!</span>
    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">efficient</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-built_in">console</span>.time(<span class="hljs-string">'Efficient Approach'</span>);
    
        <span class="hljs-comment">// Start all operations simultaneously</span>
        <span class="hljs-keyword">const</span> userPromise = fetchUser();        <span class="hljs-comment">// starts immediately</span>
        <span class="hljs-keyword">const</span> postsPromise = fetchPosts();      <span class="hljs-comment">// starts immediately</span>
        <span class="hljs-keyword">const</span> commentsPromise = fetchComments(); <span class="hljs-comment">// starts immediately</span>
    
        <span class="hljs-comment">// Wait for all to complete</span>
        <span class="hljs-keyword">const</span> [user, posts, comments] = <span class="hljs-keyword">await</span> <span class="hljs-built_in">Promise</span>.all([
            userPromise,
            postsPromise, 
            commentsPromise
        ]);
        <span class="hljs-comment">// Total time: ~500ms (longest individual operation)</span>
    
        <span class="hljs-built_in">console</span>.timeEnd(<span class="hljs-string">'Efficient Approach'</span>);
        <span class="hljs-keyword">return</span> { user, posts, comments };
    }
    

    Advanced example with error handling:

    javascriptasync <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">efficientWithErrorHandling</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">try</span> {
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Starting parallel data fetch...'</span>);
    
            <span class="hljs-comment">// Start all operations and handle individual failures</span>
            <span class="hljs-keyword">const</span> results = <span class="hljs-keyword">await</span> <span class="hljs-built_in">Promise</span>.allSettled([
                fetchUser().catch(<span class="hljs-function"><span class="hljs-params">error</span> =></span> ({ <span class="hljs-attr">error</span>: error.message, <span class="hljs-attr">type</span>: <span class="hljs-string">'user'</span> })),
                fetchPosts().catch(<span class="hljs-function"><span class="hljs-params">error</span> =></span> ({ <span class="hljs-attr">error</span>: error.message, <span class="hljs-attr">type</span>: <span class="hljs-string">'posts'</span> })),
                fetchComments().catch(<span class="hljs-function"><span class="hljs-params">error</span> =></span> ({ <span class="hljs-attr">error</span>: error.message, <span class="hljs-attr">type</span>: <span class="hljs-string">'comments'</span> }))
            ]);
    
            <span class="hljs-comment">// Process results and handle partial failures</span>
            <span class="hljs-keyword">const</span> [userResult, postsResult, commentsResult] = results;
    
            <span class="hljs-keyword">return</span> {
                <span class="hljs-attr">user</span>: userResult.status === <span class="hljs-string">'fulfilled'</span> ? userResult.value : <span class="hljs-literal">null</span>,
                <span class="hljs-attr">posts</span>: postsResult.status === <span class="hljs-string">'fulfilled'</span> ? postsResult.value : [],
                <span class="hljs-attr">comments</span>: commentsResult.status === <span class="hljs-string">'fulfilled'</span> ? commentsResult.value : [],
                <span class="hljs-attr">errors</span>: results
                    .filter(<span class="hljs-function"><span class="hljs-params">result</span> =></span> result.status === <span class="hljs-string">'rejected'</span> || result.value?.error)
                    .map(<span class="hljs-function"><span class="hljs-params">result</span> =></span> result.reason || result.value?.error)
            };
    
        } <span class="hljs-keyword">catch</span> (error) {
            <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Failed to fetch data:'</span>, error);
            <span class="hljs-keyword">throw</span> error;
        }
    }
    

    Why this is better:

    • Operations run in parallel, reducing total execution time

    • Network and CPU resources are used more efficiently

    • The application feels more responsive to users

    • Can handle partial failures gracefully

    1. Don’t Mix Patterns Unnecessarily

    Mixing Promise chains with async/await in the same function creates inconsistent code that’s harder to read, debug, and maintain. It can also lead to subtle bugs and makes the code flow less predictable.

    Avoid this:

    javascript<span class="hljs-comment">// Mixing async/await with .then() - this is confusing!</span>
    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mixedPattern</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> fetchData();
    
        <span class="hljs-comment">// Suddenly switching to Promise chain style</span>
        <span class="hljs-keyword">return</span> processData(data).then(<span class="hljs-function"><span class="hljs-params">result</span> =></span> {
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Processing complete'</span>);
            <span class="hljs-keyword">return</span> saveResult(result);
        }).then(<span class="hljs-function"><span class="hljs-params">savedResult</span> =></span> {
            <span class="hljs-keyword">return</span> savedResult.id;
        }).catch(<span class="hljs-function"><span class="hljs-params">error</span> =></span> {
            <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error in promise chain:'</span>, error);
            <span class="hljs-keyword">throw</span> error;
        });
    }
    

    Why this is problematic:

    • Inconsistent error handling patterns (try/catch vs .catch())

    • Harder to follow the execution flow

    • Different debugging experiences

    • More opportunities for bugs

    • Team members need to understand multiple patterns

    This often happens when developers are working with existing Promise-based code and try to gradually introduce async/await without fully converting the function.

    Do this instead:

    javascript<span class="hljs-comment">// Consistent async/await - much clearer!</span>
    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">consistentPattern</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> fetchData();
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Data fetched'</span>);
    
            <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> processData(data);
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Processing complete'</span>);
    
            <span class="hljs-keyword">const</span> savedResult = <span class="hljs-keyword">await</span> saveResult(result);
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Result saved'</span>);
    
            <span class="hljs-keyword">return</span> savedResult.id;
        } <span class="hljs-keyword">catch</span> (error) {
            <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error in async function:'</span>, error);
            <span class="hljs-keyword">throw</span> error;
        }
    }
    

    Alternative consistent Promise approach:

    javascript<span class="hljs-comment">// If you prefer Promises, be consistent with that too</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">consistentPromisePattern</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">return</span> fetchData()
            .then(<span class="hljs-function"><span class="hljs-params">data</span> =></span> {
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Data fetched'</span>);
                <span class="hljs-keyword">return</span> processData(data);
            })
            .then(<span class="hljs-function"><span class="hljs-params">result</span> =></span> {
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Processing complete'</span>);
                <span class="hljs-keyword">return</span> saveResult(result);
            })
            .then(<span class="hljs-function"><span class="hljs-params">savedResult</span> =></span> {
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Result saved'</span>);
                <span class="hljs-keyword">return</span> savedResult.id;
            })
            .catch(<span class="hljs-function"><span class="hljs-params">error</span> =></span> {
                <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error in promise chain:'</span>, error);
                <span class="hljs-keyword">throw</span> error;
            });
    }
    

    Use Descriptive Variable Names in Async Functions

    javascript<span class="hljs-comment">// Poor naming</span>
    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">process</span>(<span class="hljs-params">id</span>) </span>{
        <span class="hljs-keyword">const</span> d = <span class="hljs-keyword">await</span> fetch(id);
        <span class="hljs-keyword">const</span> r = <span class="hljs-keyword">await</span> transform(d);
        <span class="hljs-keyword">return</span> r;
    }
    
    <span class="hljs-comment">// Better naming</span>
    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">processUserProfile</span>(<span class="hljs-params">userId</span>) </span>{
        <span class="hljs-keyword">const</span> userData = <span class="hljs-keyword">await</span> fetchUserData(userId);
        <span class="hljs-keyword">const</span> transformedProfile = <span class="hljs-keyword">await</span> transformUserData(userData);
        <span class="hljs-keyword">return</span> transformedProfile;
    }
    

    Handle Timeouts for Long-Running Operations

    javascript<span class="hljs-comment">// Add timeout wrapper for reliability</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withTimeout</span>(<span class="hljs-params">promise, timeoutMs</span>) </span>{
        <span class="hljs-keyword">const</span> timeout = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">_, reject</span>) =></span> {
            <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =></span> reject(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Operation timed out'</span>)), timeoutMs);
        });
    
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">Promise</span>.race([promise, timeout]);
    }
    
    <span class="hljs-comment">// Usage</span>
    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">reliableDataFetch</span>(<span class="hljs-params">userId</span>) </span>{
        <span class="hljs-keyword">try</span> {
            <span class="hljs-comment">// Timeout after 5 seconds</span>
            <span class="hljs-keyword">const</span> userData = <span class="hljs-keyword">await</span> withTimeout(fetchUserData(userId), <span class="hljs-number">5000</span>);
            <span class="hljs-keyword">return</span> userData;
        } <span class="hljs-keyword">catch</span> (error) {
            <span class="hljs-keyword">if</span> (error.message === <span class="hljs-string">'Operation timed out'</span>) {
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Request timed out, using cached data'</span>);
                <span class="hljs-keyword">return</span> getCachedUserData(userId);
            }
            <span class="hljs-keyword">throw</span> error;
        }
    }
    

    Making the Right Choice

    Here’s a decision framework to help you choose:

    Choose Promises When:

    • Working with libraries that return Promises

    • Writing functional programming style code

    • Creating utility functions that other code will chain

    • You need fine-grained control over Promise behavior

    • Working with existing Promise-based codebases

    Choose Async/Await When:

    • Writing new application code

    • You need sequential operations with a clear flow

    • Complex error handling is required

    • Working with conditional asynchronous logic

    • Code readability is a priority

    • You’re building modern JavaScript applications

    Consider Both When:

    • Using Promise.all(), Promise.race(), or Promise.allSettled()

    • Building complex asynchronous flows

    • You need both the power of Promises and the readability of Async/Await

    Conclusion

    Both Promises and Async/Await are powerful tools for handling asynchronous operations in JavaScript. Promises provide flexibility and fine-grained control, while Async/Await offers cleaner, more readable code that’s easier to debug and maintain.

    In modern JavaScript development, Async/Await is generally preferred for application code due to its readability and ease of use. However, understanding Promises is still crucial since Async/Await is built on top of them, and many libraries and APIs still use Promises directly.

    The key is to understand both approaches and choose the one that best fits your specific use case, team preferences, and project requirements. Remember that you can always mix both approaches when it makes sense – use Async/Await for your main application logic and Promises for utility functions and library integrations.

    By mastering both techniques very well, you will be well-equipped to handle any asynchronous programming challenge in JavaScript.

    Source: freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More 

    Facebook Twitter Reddit Email Copy Link
    Previous ArticleLock down your AT&T account to prevent SIM swapping attacks – here’s how
    Next Article How to Fetch API Data in React Using Axios

    Related Posts

    Development

    Using phpinfo() to Debug Common and Not-so-Common PHP Errors and Warnings

    September 28, 2025
    Development

    Mastering PHP File Uploads: A Guide to php.ini Settings and Code Examples

    September 28, 2025
    Leave A Reply Cancel Reply

    For security, use of Google's reCAPTCHA service is required which is subject to the Google Privacy Policy and Terms of Use.

    Continue Reading

    VideoDubber’s YouTube Dislike Checker

    Web Development

    AI in Retail: The Playbook for Retail Transformation

    Development

    A Step-by-Step Implementation Tutorial for Building Modular AI Workflows Using Anthropic’s Claude Sonnet 3.7 through API and LangGraph

    Machine Learning

    Time to Rethink Db2 Disaster Recovery

    Databases

    Highlights

    Best Hotels in Ranthambore: Luxury Stays Near the Tiger Reserve

    June 30, 2025

    Post Content Source: Read More 

    Declarative vs Imperative with PoM?

    August 4, 2025

    AI Assistant Demo & Tips for Enterprise Projects

    May 15, 2025

    Pakistan-Linked Hackers Expand Targets in India with CurlBack RAT and Spark RAT

    April 14, 2025
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

    Type above and press Enter to search. Press Esc to cancel.