Why Use Promises?
Promises provide a structured way to handle asynchronous tasks, making code more readable and preventing "callback hell." They simplify working with tasks that return future values, like API calls or delayed operations.
Promise Basics
A JavaScript Promise
represents a future value and has three states:
- Pending - Initial state, still awaiting resolution.
- Fulfilled - Task completed successfully.
- Rejected - Task failed with an error.
Key Methods
-
then()
: Runs when the promise is fulfilled, passing its result. -
catch()
: Catches errors from any rejected promises. -
finally()
: Runs after the promise settles, regardless of outcome.
Promise Chaining
Promise chaining allows sequential async tasks to be run in order:
fetchData('url')
.then(result => fetchData('anotherURL'))
.then(result => console.log(result))
.catch(error => console.error('Error:', error.message));
Error Handling in Chains
A single catch() at the end captures any rejection in the chain. Multiple catch() statements can also be used to handle specific errors after certain promises in the chain.
Real-World Uses
Promises are ideal for async tasks like API requests, timers, and file operations.
Advanced Methods
Promise.all(): Waits for all promises to complete. If any promise rejects, Promise.all() fails entirely with that rejection.
Promise.race(): Resolves or rejects with the first settled promise, whether it is resolved or rejected.
Promise.any(): Resolves with the first fulfilled promise or fails if all promises reject.
Summary
Promises simplify async workflows by replacing nested callbacks, offering a linear structure for tasks, and providing centralized error handling. They enable clean, readable async code, especially for complex JavaScript operations.