<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Abhijeet Chaudhari</title>
    <description>The latest articles on DEV Community by Abhijeet Chaudhari (@abhijeet_chaudhari_a).</description>
    <link>https://dev.to/abhijeet_chaudhari_a</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3329925%2Fc29772d3-ddae-4f9d-ad0c-ab47e145a6ea.jpg</url>
      <title>DEV Community: Abhijeet Chaudhari</title>
      <link>https://dev.to/abhijeet_chaudhari_a</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/abhijeet_chaudhari_a"/>
    <language>en</language>
    <item>
      <title>DynamoDB: GSI, LSI, and related design ideas</title>
      <dc:creator>Abhijeet Chaudhari</dc:creator>
      <pubDate>Tue, 11 Aug 2026 10:21:10 +0000</pubDate>
      <link>https://dev.to/abhijeet_chaudhari_a/dynamodb-gsi-lsi-and-related-design-ideas-3fll</link>
      <guid>https://dev.to/abhijeet_chaudhari_a/dynamodb-gsi-lsi-and-related-design-ideas-3fll</guid>
      <description>&lt;p&gt;This note explains the important DynamoDB concepts in a simple and practical way.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First idea: think in access patterns
Before designing a DynamoDB table, ask:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What questions will the application ask?&lt;br&gt;
Which fields will be used for filtering and sorting?&lt;br&gt;
Which operations must be fast and cheap?&lt;br&gt;
Which data can be deleted or archived later?&lt;br&gt;
A good DynamoDB design is not only about storing data. It is about designing the table around the way the application will read it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What is a GSI?
GSI = Global Secondary Index&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Simple meaning&lt;br&gt;
A GSI gives you a second way to access the same table.&lt;/p&gt;

&lt;p&gt;It lets you query data using:&lt;/p&gt;

&lt;p&gt;a different partition key&lt;br&gt;
a different sort key&lt;br&gt;
or both&lt;br&gt;
Where to use GSI&lt;br&gt;
Use a GSI when:&lt;/p&gt;

&lt;p&gt;the main table key does not match your common query pattern&lt;br&gt;
you need to query by another field often&lt;br&gt;
you want to avoid scanning the whole table&lt;br&gt;
How it works&lt;br&gt;
DynamoDB keeps a separate index structure for the GSI.&lt;/p&gt;

&lt;p&gt;The GSI stores:&lt;/p&gt;

&lt;p&gt;the indexed attributes you choose&lt;br&gt;
the primary key of the GSI&lt;br&gt;
a pointer back to the original item in the base table&lt;br&gt;
When to use GSI&lt;br&gt;
Use it when you need:&lt;/p&gt;

&lt;p&gt;user lookup by email&lt;br&gt;
order lookup by status and created time&lt;br&gt;
product lookup by category&lt;br&gt;
Example&lt;br&gt;
Suppose your table stores orders by order ID.&lt;/p&gt;

&lt;p&gt;You often need to find orders by customer ID and date.&lt;/p&gt;

&lt;p&gt;In that case, a GSI can help.&lt;/p&gt;

&lt;p&gt;Pros&lt;br&gt;
Flexible&lt;br&gt;
Good for alternate access patterns&lt;br&gt;
Helps avoid full table scans&lt;br&gt;
Cons&lt;br&gt;
Costs extra&lt;br&gt;
Writes are slower because data is written to the base table and the index&lt;br&gt;
You must design it carefully&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What is an LSI?
LSI = Local Secondary Index&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Simple meaning&lt;br&gt;
An LSI gives you another sort key, but it must use the same partition key as the base table.&lt;/p&gt;

&lt;p&gt;Where to use LSI&lt;br&gt;
Use an LSI when:&lt;/p&gt;

&lt;p&gt;you want to query items inside the same partition using a different sort key&lt;br&gt;
you already use the same partition key in your main access pattern&lt;br&gt;
How it works&lt;br&gt;
The LSI is built inside the same partition as the base table.&lt;/p&gt;

&lt;p&gt;It allows:&lt;/p&gt;

&lt;p&gt;same partition key&lt;br&gt;
different sort key&lt;br&gt;
When to use LSI&lt;br&gt;
Use it when you need:&lt;/p&gt;

&lt;p&gt;query all items for one customer by different date ranges&lt;br&gt;
sort items in a partition by another field&lt;br&gt;
Example&lt;br&gt;
If your base table uses:&lt;/p&gt;

&lt;p&gt;partition key = customer ID&lt;br&gt;
sort key = order date&lt;br&gt;
And you want to query by order status inside the same customer partition, an LSI can help.&lt;/p&gt;

&lt;p&gt;Pros&lt;br&gt;
Good for querying within the same partition&lt;br&gt;
No need to use a different partition key&lt;br&gt;
Useful when the access pattern is strongly tied to the same partition key&lt;br&gt;
Cons&lt;br&gt;
Limited because it must use the same partition key as the base table&lt;br&gt;
Not as flexible as a GSI&lt;br&gt;
Still costs extra storage and write capacity&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;GSI vs LSI
Topic   GSI LSI
Full form   Global Secondary Index  Local Secondary Index
Partition key   Can be different from base table    Must be the same as base table
Sort key    Can be different    Can be different
Flexibility High    Medium
Best for    Different access patterns   Same partition, different sort behavior
Cost    Higher  Lower than GSI but still extra
Write overhead  Higher  Lower than GSI
Use case    Search by another field Query within same partition by another sort key&lt;/li&gt;
&lt;li&gt;What is a sparse index?
A sparse index means an index only contains items that have the indexed attribute present.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Simple meaning&lt;br&gt;
If an item does not have the indexed field, it will not appear in the index.&lt;/p&gt;

&lt;p&gt;Why it is useful&lt;br&gt;
This is useful when:&lt;/p&gt;

&lt;p&gt;some items do not need the index&lt;br&gt;
you want to avoid storing unnecessary index data&lt;br&gt;
you want to make the index more focused&lt;br&gt;
Example&lt;br&gt;
If you create a GSI on a field called deletedAt:&lt;/p&gt;

&lt;p&gt;items with no deletedAt value are not in the index&lt;br&gt;
items with a deletedAt value appear in the index&lt;br&gt;
This is a simple and powerful way to model optional data.&lt;/p&gt;

&lt;p&gt;Important note&lt;br&gt;
Sparse indexes are not the same as a full table scan. They are still indexes, but only for items that meet the condition.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Query vs Scan
Query
A query is selective and targeted.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It reads only the items you want based on the key.&lt;/p&gt;

&lt;p&gt;Scan&lt;br&gt;
A scan reads the whole table, or a large portion of it.&lt;/p&gt;

&lt;p&gt;Easy comparison&lt;br&gt;
Operation   What it does    Speed   Cost    Best use&lt;br&gt;
Query   Reads specific items using key values   Fast    Lower   Normal lookup by key&lt;br&gt;
Scan    Reads many items in the table   Slower  Higher  Full review or reporting&lt;br&gt;
Rule of thumb&lt;br&gt;
Use query whenever possible. Use scan only when you truly need to read a large set of data.&lt;/p&gt;

&lt;p&gt;Why this matters&lt;br&gt;
A scan can become expensive quickly if the table grows.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hot partitions
A hot partition happens when one partition key gets too much traffic.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Simple meaning&lt;br&gt;
One logical partition becomes a bottleneck.&lt;/p&gt;

&lt;p&gt;Example&lt;br&gt;
If many requests use the same user ID, that partition may get overloaded.&lt;/p&gt;

&lt;p&gt;Symptoms&lt;br&gt;
throttling&lt;br&gt;
slow writes&lt;br&gt;
slow reads&lt;br&gt;
uneven performance&lt;br&gt;
How to reduce hot partitions&lt;br&gt;
use a more distributed partition key&lt;br&gt;
add a random suffix to the partition key&lt;br&gt;
use a composite key with a stable prefix and a unique suffix&lt;br&gt;
avoid very popular single values as partition keys&lt;br&gt;
Example&lt;br&gt;
Instead of using only customerId as the partition key, use:&lt;/p&gt;

&lt;p&gt;customerId#region&lt;br&gt;
or customerId#tenantId&lt;br&gt;
or customerId#randomSuffix&lt;br&gt;
This spreads traffic better.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sharding in simple words
DynamoDB does not expose manual sharding in the same way as a traditional database cluster.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Instead, DynamoDB automatically partitions your data across internal storage partitions.&lt;/p&gt;

&lt;p&gt;Simple idea&lt;br&gt;
The partition key is the main distribution mechanism.&lt;/p&gt;

&lt;p&gt;If the partition key is well designed, data and traffic are spread across many internal partitions.&lt;/p&gt;

&lt;p&gt;Why it matters&lt;br&gt;
A poor partition key can create a hot partition. A good partition key can spread load evenly.&lt;/p&gt;

&lt;p&gt;Rule of thumb&lt;br&gt;
Choose a partition key that has many possible values and is used evenly.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Adaptive capacity
Adaptive capacity is a DynamoDB feature that helps during temporary traffic spikes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Simple meaning&lt;br&gt;
If one partition is busy for a short time, DynamoDB can temporarily borrow capacity from other partitions that are not fully using their limit.&lt;/p&gt;

&lt;p&gt;Why it helps&lt;br&gt;
It reduces throttling during short bursts.&lt;/p&gt;

&lt;p&gt;Important note&lt;br&gt;
It is not a permanent solution for a badly designed key structure. It helps with temporary imbalance, but good design is still important.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Auto scaling
Auto scaling automatically changes the read/write capacity based on demand.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Simple meaning&lt;br&gt;
When traffic increases, DynamoDB increases capacity. When traffic decreases, capacity can be reduced.&lt;/p&gt;

&lt;p&gt;Best use&lt;br&gt;
Use auto scaling when:&lt;/p&gt;

&lt;p&gt;traffic changes over time&lt;br&gt;
you want less manual tuning&lt;br&gt;
workloads are somewhat predictable but not fixed&lt;br&gt;
Important note&lt;br&gt;
Auto scaling helps, but it does not replace good key design. A poor key design may still cause hot partitions.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;TTL (Time to Live)
TTL = Time to Live&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Simple meaning&lt;br&gt;
You can mark an item with an expiration time.&lt;/p&gt;

&lt;p&gt;After that time, DynamoDB deletes the item automatically.&lt;/p&gt;

&lt;p&gt;Where to use TTL&lt;br&gt;
Use TTL when:&lt;/p&gt;

&lt;p&gt;data expires naturally&lt;br&gt;
you want to delete old sessions&lt;br&gt;
you want to remove temporary logs or cache-like data&lt;br&gt;
Example&lt;br&gt;
A session token can be stored with an expiry timestamp. When the time passes, the item is removed automatically.&lt;/p&gt;

&lt;p&gt;Benefit&lt;br&gt;
It helps reduce storage and cleanup effort.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;PITR (Point-in-Time Recovery)
PITR = Point-in-Time Recovery&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Simple meaning&lt;br&gt;
PITR lets you restore a table to any point in the last 35 days.&lt;/p&gt;

&lt;p&gt;Where to use PITR&lt;br&gt;
Use PITR when:&lt;/p&gt;

&lt;p&gt;accidental deletes happen&lt;br&gt;
data is updated incorrectly&lt;br&gt;
you want a safety net for recovery&lt;br&gt;
Why it matters&lt;br&gt;
It is a protection feature, not a normal query feature.&lt;/p&gt;

&lt;p&gt;Good rule&lt;br&gt;
Use PITR for important data, not just for temporary data.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Optimistic locking
Optimistic locking helps prevent lost updates.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Simple meaning&lt;br&gt;
You store a version number in the item.&lt;/p&gt;

&lt;p&gt;Before updating, you check that the version still matches.&lt;/p&gt;

&lt;p&gt;If another process changed it already, the update fails.&lt;/p&gt;

&lt;p&gt;Why it is useful&lt;br&gt;
It prevents two writers from overwriting each other by accident.&lt;/p&gt;

&lt;p&gt;Example&lt;br&gt;
If two clients update the same item at the same time:&lt;/p&gt;

&lt;p&gt;one update succeeds&lt;br&gt;
the other update sees the version mismatch&lt;br&gt;
it can retry safely&lt;br&gt;
Easy idea&lt;br&gt;
Think of it as: “I will update only if nothing changed since I read it.”&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pattern thinking
This is one of the most important ideas.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What is pattern thinking?&lt;br&gt;
Pattern thinking means designing your table around the real access patterns of the application.&lt;/p&gt;

&lt;p&gt;Instead of asking:&lt;/p&gt;

&lt;p&gt;“How do I store this data?”&lt;br&gt;
Ask:&lt;/p&gt;

&lt;p&gt;“How will this data be read?”&lt;br&gt;
“How will it be filtered?”&lt;br&gt;
“How will it be sorted?”&lt;br&gt;
“What are the most common requests?”&lt;br&gt;
Good pattern thinking example&lt;br&gt;
If the app often needs to find orders by customer and date, design the table around that pattern.&lt;/p&gt;

&lt;p&gt;If the app often needs to find products by category, use a GSI for category-based access.&lt;/p&gt;

&lt;p&gt;Simple rule&lt;br&gt;
Design the table for the questions the application asks most often.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Practical decision guide
Situation   Best choice
Need a different partition key  GSI
Need a different sort key within the same partition LSI
Only some items should appear in the index  Sparse index
Need fast targeted reads    Query
Need to read everything Scan
One partition gets too much traffic Rework partition key / use better distribution
Need automatic capacity growth  Auto scaling
Need automatic cleanup of old data  TTL
Need recovery to an earlier state   PITR
Need to prevent lost updates    Optimistic locking&lt;/li&gt;
&lt;li&gt;Very short summary
GSI gives a new access path for your data.
LSI gives another sort key inside the same partition.
Sparse indexes only include items with the indexed field.
Query is better than Scan for normal access.
Hot partitions happen when one partition gets too much traffic.
Good partition key design is very important.
Auto scaling helps with growth.
TTL removes old data automatically.
PITR helps recover from mistakes.
Optimistic locking prevents overwriting updates.
Pattern thinking is the real key to good DynamoDB design.&lt;/li&gt;
&lt;li&gt;One easy mental model
Think of DynamoDB design like this:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Table = the main place where data lives&lt;br&gt;
GSI = a shortcut road to the same data&lt;br&gt;
LSI = a different lane inside the same road&lt;br&gt;
Query = using the right key to find what you want&lt;br&gt;
Scan = walking through everything&lt;br&gt;
Hot partition = traffic jam on one road&lt;br&gt;
TTL = automatic cleanup&lt;br&gt;
PITR = backup and restore safety net&lt;br&gt;
If you remember only one thing, remember this:&lt;/p&gt;

&lt;p&gt;“Design for the access pattern, not just for the storage shape.”&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>aws</category>
      <category>database</category>
    </item>
    <item>
      <title>Understanding Asynchronous JavaScript: From Callbacks to Async/Await</title>
      <dc:creator>Abhijeet Chaudhari</dc:creator>
      <pubDate>Mon, 07 Jul 2025 05:02:10 +0000</pubDate>
      <link>https://dev.to/abhijeet_chaudhari_a/understanding-asynchronous-javascript-from-callbacks-to-asyncawait-2h9o</link>
      <guid>https://dev.to/abhijeet_chaudhari_a/understanding-asynchronous-javascript-from-callbacks-to-asyncawait-2h9o</guid>
      <description>&lt;h2&gt;
  
  
  Understanding Callbacks, Promises, Async/Await
&lt;/h2&gt;

&lt;p&gt;JavaScript operates on a single-threaded model, handling one task at a time. Yet, modern web applications often require handling multiple operations concurrently, such as data fetching, file operations, or user interactions. To address this, JavaScript employs asynchronous programming techniques. This post delves into three fundamental asynchronous JavaScript concepts: callbacks, promises, and async/await.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Callbacks: The Traditional Method&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Callbacks are functions passed as arguments to other functions and executed upon completion of an operation. They are a basic approach to managing asynchronous tasks in JavaScript.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example of Callbacks&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function retrieveData(callback) {
    setTimeout(() =&amp;gt; {
        console.log("Successfully retrieved data from the server");
        callback();
    }, 2000);
}

function handleData() {
    console.log("Data processing initiated...");
}

retrieveData(handleData);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, retrieve Data simulates data retrieval with a delay. Once the data is ready, it invokes the handleData function.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Challenges with Callbacks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While callbacks are simple, they can lead to "&lt;code&gt;callback hell&lt;/code&gt;," where multiple nested callbacks make the code complex and difficult to manage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function retrieveData(callback) {
    setTimeout(() =&amp;gt; {
        console.log("Data retrieved from the server");
        callback();
    }, 2000);
}

function handleData(callback) {
    console.log("Processing data...");
    callback();
}

function showData() {
    console.log("Data displayed...");
}

retrieveData(() =&amp;gt; {
    handleData(() =&amp;gt; {
        showData();
    });
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Promises: A More Structured Approach
&lt;/h2&gt;

&lt;p&gt;Promises provide a more organized way to handle asynchronous operations. A promise represents a value that may be available immediately, later, or never, and can be in one of three states: &lt;code&gt;pending, fulfilled, or rejected&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pending: **The initial state of a promise; neither fulfilled nor rejected.&lt;br&gt;
**Fulfilled:&lt;/strong&gt; The state of a promise representing a successful operation.&lt;br&gt;
&lt;strong&gt;Rejected:&lt;/strong&gt; The state of a promise representing a failed operation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example of Promises&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function retrieveData() {
    return new Promise((resolve, reject) =&amp;gt; {
        setTimeout(() =&amp;gt; {
            resolve("Data retrieved from the server");
        }, 2000);
    });
}

retrieveData()
    .then((message) =&amp;gt; {
        console.log(message);
        return "Data processing initiated...";
    })
    .then((message) =&amp;gt; {
        console.log(message);
        return "Data displayed...";
    })
    .then((message) =&amp;gt; {
        console.log(message);
    })
    .catch((error) =&amp;gt; {
        console.error("An error occurred:", error);
    });

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, retrieveData returns a promise. The &lt;code&gt;.then&lt;/code&gt; method handles the resolved value, allowing for chaining of asynchronous operations in a clear manner.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages of Promises&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chaining:&lt;/strong&gt; Promises can be chained, making the code more readable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Handling:&lt;/strong&gt; Promises use the &lt;u&gt;catch method for error handling&lt;/u&gt;, which simplifies managing errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Async/Await: Simplifying Asynchronous Code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Async/await, introduced in ES2017, allows writing asynchronous code in a synchronous style. It is built on promises and &lt;u&gt;enhances code readability&lt;/u&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example of Async/Await&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function retrieveData() {
    return new Promise((resolve, reject) =&amp;gt; {
        setTimeout(() =&amp;gt; {
            resolve("Data retrieved from the server");
        }, 2000);
    });
}

async function manageData() {
    try {
        const data = await retrieveData();
        console.log(data);
        console.log("Data processing initiated...");
        console.log("Data displayed...");
    } catch (error) {
        console.error("An error occurred:", error);
    }
}

manageData();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, retrieveData returns a promise, and manageData uses the &lt;code&gt;async&lt;/code&gt; keyword. The &lt;code&gt;await&lt;/code&gt; keyword pauses execution until the promise is resolved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Benefits of Async/Await&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Readability:&lt;/strong&gt; Async/await makes asynchronous code appear synchronous, improving readability.&lt;br&gt;
&lt;strong&gt;Error Handling:&lt;/strong&gt; It uses try/catch blocks, which are familiar to those experienced with synchronous code.&lt;/p&gt;
&lt;h2&gt;
  
  
  More About Promises - Types and features
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Handling Multiple Promises&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When dealing with multiple asynchronous operations, JavaScript provides several methods to manage them collectively. These methods are particularly useful when you need to coordinate &lt;code&gt;multiple promises&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Promise.all&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Promise.all&lt;/code&gt; takes an iterable of promises and returns a single promise that resolves when all of the input promises have resolved, or rejects if any of the input promises reject. This method is useful when you need all operations to complete successfully.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve) =&amp;gt; {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then((values) =&amp;gt; {
  console.log(values); // Output: [3, 42, "foo"]
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, Promise.all waits for all promises to resolve and then returns an array of their results&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Promise.race&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Promise.race&lt;/code&gt; returns a promise that settles as soon as the first promise in the iterable settles (either resolves or rejects). This can be useful for setting timeouts or when you need the first response from multiple sources.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const promise1 = new Promise((resolve) =&amp;gt; setTimeout(resolve, 500, 'one'));
const promise2 = new Promise((resolve) =&amp;gt; setTimeout(resolve, 100, 'two'));

Promise.race([promise1, promise2]).then((value) =&amp;gt; {
  console.log(value); // Output: "two"
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, promise2 resolves faster than promise1, so Promise.race returns the result of promise2.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Promise.allSettled&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Promise.allSettled&lt;/code&gt; returns a promise that resolves after all the given promises have either resolved or rejected, with an array of objects that each describes the outcome of each promise. This is useful when you need to know the result of each promise regardless of whether it resolved or rejected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const promise1 = Promise.resolve(3);
const promise2 = new Promise((_, reject) =&amp;gt; setTimeout(reject, 100, 'Error'));
const promise3 = new Promise((resolve) =&amp;gt; setTimeout(resolve, 200, 'foo'));

Promise.allSettled([promise1, promise2, promise3]).then((results) =&amp;gt; {
  results.forEach((result) =&amp;gt; console.log(result.status));
  // Output:
  // "fulfilled"
  // "rejected"
  // "fulfilled"
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, Promise.allSettled waits for all promises to settle and returns an array of objects with the status and value (or reason for rejection) of each promise.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;&lt;u&gt;Asynchronous programming is crucial in JavaScript, enabling efficient handling of multiple tasks without blocking the main thread&lt;/u&gt;. Callbacks, while straightforward, can lead to complex code. Promises offer a cleaner approach with better readability and error handling. Async/await builds on promises, providing an even more intuitive syntax for writing asynchronous code. Mastering these concepts will help you write more efficient and maintainable JavaScript applications.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
