Promise
Definition
A Promise is an object representing the eventual completion or failure of an asynchronous operation. Promises provide a cleaner way to handle asynchronous code compared to callbacks.
const promise = new Promise((resolve, reject) => {})
promise instanceof Promise // true
Creating Promises
// Basic promise
const promise = new Promise((resolve, reject) => {
// Asynchronous operation
if (success) {
resolve(value)
} else {
reject(error)
}
})
Promise States
A Promise has three states:
- Pending: Initial state, neither fulfilled nor rejected
- Fulfilled: Operation completed successfully
- Rejected: Operation failed
Using Promises
then() and catch()
const promise = fetch('/api/data')
promise
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error))
Chaining
fetch('/api/data')
.then(response => response.json())
.then(data => processData(data))
.then(result => saveResult(result))
.catch(error => handleError(error))
Promise Methods
Promise.resolve()
Creates a resolved promise:
Promise.resolve(42)
.then(value => console.log(value)) // 42
Promise.reject()
Creates a rejected promise:
Promise.reject(new Error('Failed'))
.catch(error => console.error(error)) // Error: Failed
Promise.all()
Waits for all promises to resolve:
const p1 = Promise.resolve(1)
const p2 = Promise.resolve(2)
const p3 = Promise.resolve(3)
Promise.all([p1, p2, p3])
.then(values => console.log(values)) // [1, 2, 3]
If any promise rejects, Promise.all() rejects:
const p1 = Promise.resolve(1)
const p2 = Promise.reject(new Error('Failed'))
const p3 = Promise.resolve(3)
Promise.all([p1, p2, p3])
.catch(error => console.error(error)) // Error: Failed
Promise.allSettled()
Waits for all promises to settle (resolve or reject):
const p1 = Promise.resolve(1)
const p2 = Promise.reject(new Error('Failed'))
const p3 = Promise.resolve(3)
Promise.allSettled([p1, p2, p3])
.then(results => {
// [
// { status: 'fulfilled', value: 1 },
// { status: 'rejected', reason: Error },
// { status: 'fulfilled', value: 3 }
// ]
})
Promise.race()
Returns the first settled promise:
const p1 = new Promise(resolve => setTimeout(() => resolve(1), 100))
const p2 = new Promise(resolve => setTimeout(() => resolve(2), 50))
Promise.race([p1, p2])
.then(value => console.log(value)) // 2 (faster)
Promise.any()
Returns the first fulfilled promise:
const p1 = Promise.reject(new Error('Failed'))
const p2 = Promise.resolve(2)
const p3 = Promise.resolve(3)
Promise.any([p1, p2, p3])
.then(value => console.log(value)) // 2
Async/Await
Syntactic sugar for working with promises:
async function fetchData() {
try {
const response = await fetch('/api/data')
const data = await response.json()
return data
} catch (error) {
console.error(error)
}
}
Common Patterns
Timeout
function timeout(ms) {
return new Promise((_, reject) => {
setTimeout(() => reject(new Error('Timeout')), ms)
})
}
Promise.race([fetch('/api/data'), timeout(5000)])
.then(data => console.log(data))
.catch(error => console.error(error))
Retry
function retry(fn, retries = 3) {
return fn().catch(error => {
if (retries > 0) {
return retry(fn, retries - 1)
}
throw error
})
}
Sequential Execution
async function sequential() {
const result1 = await operation1()
const result2 = await operation2(result1)
const result3 = await operation3(result2)
return result3
}
Best Practices
-
Always handle errors: Use
.catch()ortry/catchwith async/await. -
Avoid promise hell: Use async/await for cleaner code.
-
Use Promise.all() for parallel operations: When operations don't depend on each other.
-
Don't forget to return: In promise chains, return values to pass them along.
-
Handle both success and failure: Always provide error handling.