- JS Introduction
- JS Introduction
- JS Comments
- JS Variables
- JS Datatypes
- JS Operators
- JS Type Conversions
- JS Control Flow
- JS Comparisons
- JS If else
- JS If else Ladder
- JS Ternary Operator
- JS Switch
- JS For Loop
- JS For In
- JS For Of
- JS While
- JS Do While
- JS Break & Continue
- JS Functions
- JS Function Declaration
- JS Function Parameters
- JS Return Statement
- JS Function Expressions
- JS Anonymous Functions
- JS Objects
- JS Objects
- JS Object Methods
- JS Object Constructors
- JS Object Destructuring
- JS Object Prototypes
- JS Map, Filter & Reduce
- JS ES6
- JS ES6
- JS let and const
- JS Arrow Functions
- JS Template Literals
- Destructuring Assignment
- JS Spread Operator
- JS Default Parameters
- JS Classes
- JS Inheritance
- JS Map
- JS Set
- JS Async
- JS Callbacks
- JS Asynchronous
- JS Promises
- JS Async/Await
- JS HTML DOM/BOM
- JS Document Object
- JS getElementbyId
- getElementsByClassName
- JS getElementsByName
- getElementsByTagName
- JS innerHTML
- JS outerHTML
- JS Window Object
- JS History Object
- JS Navigator Object
- JS Screen Object
JavaScript Promises
Asynchronous Operations:
In JavaScript, many operations take time—fetching data from a server, reading a file, or waiting for a timer to finish. If JavaScript waited for these tasks to finish before moving on, your application would "freeze" and become unresponsive. Promises solve this by representing the eventual completion (or failure) of an asynchronous operation. They act as a placeholder for a value that isn't available yet but will be provided at some point in the future.
States:
- Pending: The initial state; the operation has not started or is still in progress.
- Fulfilled: The operation completed successfully, and the Promise now has a resulting value.
- Rejected: The operation failed, usually providing an error message or reason.
- Once a promise settles (either fulfilled or rejected), it is considered immutable—it cannot change its state or value again.
resolve() and then immediately call reject(), the rejection will be ignored because the Promise was already settled by the first call.
Chaining:
One of the biggest advantages of Promises is chaining. Before Promises, developers dealt with "Callback Hell"—nested functions that were incredibly hard to read. With Promises, you can use .then() to perform sequential tasks. The data returned from one .then() block is automatically passed as the input to the next one in the chain.
.then() block. This ensures the chain stays intact and the next link in the chain has the data it needs to continue.
Error Handling:
Promises provide a clean way to handle errors using the .catch() method. If any step in your chain fails, the execution skips the remaining .then() blocks and jumps straight to the nearest .catch(). This allows you to manage errors in one centralized place rather than checking for errors at every single step.
.catch() block at the end of your chain. This can lead to "Unhandled Promise Rejections," which can crash some environments (like Node.js) or make debugging very difficult.
Example: Creating a Promise
When you create a Promise, you provide an "executor" function that takes two arguments: resolve and reject. This is where you put your asynchronous logic.
const fetchData = new Promise((resolve, reject) => {
console.log("Fetching data from the server...");
setTimeout(() => {
const success = true;
if (success) {
const data = { id: 1, name: 'John Doe' };
// Move the promise to the 'fulfilled' state
resolve(data);
} else {
// Move the promise to the 'rejected' state
reject('Connection timeout occurred');
}
}, 2000);
});
Example: Using Promises
Once a Promise is created, we use .then() to handle the success case and .catch() to handle the failure case.
fetchData
.then(user => {
console.log('Success! User found:', user.name);
return user.id; // Passing the ID to the next .then()
})
.then(id => {
console.log('Fetching profile for ID:', id);
})
.catch(error => {
console.error('Error:', error);
});
Promise.all():
- Promise.all() is used when you need to perform multiple asynchronous tasks at the same time and wait for all of them to finish before proceeding.
- It takes an array of promises and returns a single promise.
- If all promises resolve, it returns an array of results. However, if any promise fails, the entire
Promise.all()call fails immediately.
Example: Promise.all()
Imagine you need to fetch a user's profile and their list of friends simultaneously to speed up page loading.
const fetchUser = new Promise(resolve => setTimeout(() => resolve('User Profile'), 1000));
const fetchPosts = new Promise(resolve => setTimeout(() => resolve('User Posts'), 1500));
Promise.all([fetchUser, fetchPosts])
.then(([profile, posts]) => {
console.log('Dashboard loaded:', profile, 'and', posts);
})
.catch(error => {
console.error('One of the requests failed:', error);
});
Promise.all() for independent requests (like fetching a list of categories and a list of products). If the requests depend on each other (like getting a User ID before fetching their specific orders), use regular Promise chaining instead.
Key Points
- Promises provide a structured, readable way to handle code that doesn't finish instantly.
- They offer three distinct states (Pending, Fulfilled, Rejected) to track the lifecycle of a task.
- Chaining with
.then()eliminates deeply nested callbacks and makes code maintainable. - Centralized error handling with
.catch()ensures your application can recover gracefully from network failures or logic errors. Promise.all()is a powerful tool for improving performance by running multiple tasks in parallel.