Promises: Creation, Chaining, Resolution
Promises provide a cleaner way to handle asynchronous operations compared to callbacks.
What is a Promise?
A Promise represents the eventual completion (or failure) of an asynchronous operation:
const promise = new Promise((resolve, reject) => {
// Asynchronous operation
if (success) {
resolve(value); // Fulfilled
} else {
reject(error); // Rejected
}
});
Promise States
A Promise has three states:
- Pending: Initial state, operation hasn’t completed
- Fulfilled: Operation completed successfully
- Rejected: Operation failed
// Pending
const promise = new Promise((resolve, reject) => {
// Still pending
});
// Fulfilled
const fulfilled = new Promise((resolve, reject) => {
resolve("Success!");
});
// Rejected
const rejected = new Promise((resolve, reject) => {
reject(new Error("Failed!"));
});
Creating Promises
Basic Promise
function fetchUser(userId) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (userId > 0) {
resolve({ id: userId, name: "Alice" });
} else {
reject(new Error("Invalid user ID"));
}
}, 1000);
});
}
fetchUser(1)
.then(user => console.log(user))
.catch(error => console.error(error));
Resolved/Rejected Promises
// Immediately resolved
const resolved = Promise.resolve("Success");
// Immediately rejected
const rejected = Promise.reject(new Error("Failed"));
// Resolve with value
Promise.resolve(42).then(value => console.log(value)); // 42
Consuming Promises
then() Method
promise.then(
(value) => {
// Handle success
console.log(value);
},
(error) => {
// Handle error (optional)
console.error(error);
}
);
catch() Method
promise
.then(value => console.log(value))
.catch(error => console.error(error));
finally() Method
promise
.then(value => console.log(value))
.catch(error => console.error(error))
.finally(() => {
console.log("Operation complete"); // Always runs
});
Promise Chaining
Chain multiple asynchronous operations:
function getUser(userId) {
return new Promise((resolve) => {
setTimeout(() => resolve({ id: userId, name: "Alice" }), 1000);
});
}
function getOrders(userId) {
return new Promise((resolve) => {
setTimeout(() => resolve([{ id: 1, total: 100 }]), 1000);
});
}
getUser(1)
.then(user => {
console.log("User:", user);
return getOrders(user.id);
})
.then(orders => {
console.log("Orders:", orders);
})
.catch(error => console.error(error));
Returning Values
Promise.resolve(5)
.then(value => {
console.log(value); // 5
return value * 2;
})
.then(value => {
console.log(value); // 10
return value + 3;
})
.then(value => {
console.log(value); // 13
});
Returning Promises
function step1() {
return Promise.resolve("Step 1 complete");
}
function step2(result) {
return new Promise((resolve) => {
setTimeout(() => resolve(`${result} -> Step 2 complete`), 1000);
});
}
function step3(result) {
return Promise.resolve(`${result} -> Step 3 complete`);
}
step1()
.then(result => step2(result))
.then(result => step3(result))
.then(result => console.log(result));
Promise Utilities
Promise.all()
Wait for all promises to resolve:
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve) => setTimeout(() => resolve("foo"), 100));
const promise3 = fetch("https://api.example.com/data").then(r => r.json());
Promise.all([promise1, promise2, promise3])
.then(values => {
console.log(values); // [3, "foo", data]
})
.catch(error => console.error(error));
Promise.race()
Return first promise to settle:
const promise1 = new Promise((resolve) => setTimeout(() => resolve("First"), 500));
const promise2 = new Promise((resolve) => setTimeout(() => resolve("Second"), 100));
Promise.race([promise1, promise2])
.then(value => console.log(value)); // "Second"
Promise.allSettled()
Wait for all promises to settle (resolve or reject):
const promises = [
Promise.resolve(1),
Promise.reject(new Error("Failed")),
Promise.resolve(3)
];
Promise.allSettled(promises)
.then(results => {
console.log(results);
// [
// { status: "fulfilled", value: 1 },
// { status: "rejected", reason: Error },
// { status: "fulfilled", value: 3 }
// ]
});
Promise.any()
Return first fulfilled promise:
const promises = [
Promise.reject(new Error("Error 1")),
Promise.resolve("Success"),
Promise.reject(new Error("Error 2"))
];
Promise.any(promises)
.then(value => console.log(value)) // "Success"
.catch(error => console.error(error));
Error Handling
Catching Errors
function fetchData(url) {
return fetch(url)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.catch(error => {
console.error("Fetch error:", error);
throw error; // Re-throw to propagate
});
}
Error Propagation
Promise.resolve(1)
.then(value => {
throw new Error("Error in step 1");
})
.then(value => {
console.log(value); // Never runs
})
.catch(error => {
console.error(error); // Catches error from step 1
});
Practical Examples
Retry Logic
function retryPromise(fn, maxRetries = 3) {
return fn().catch(error => {
if (maxRetries <= 1) {
throw error;
}
console.log(`Retry ${4 - maxRetries}...`);
return retryPromise(fn, maxRetries - 1);
});
}
retryPromise(() => {
return Math.random() > 0.7
? Promise.resolve("Success")
: Promise.reject(new Error("Failed"));
}, 3)
.then(result => console.log(result))
.catch(error => console.error("All retries failed"));
Timeout
function withTimeout(promise, ms) {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error("Timeout")), ms)
)
]);
}
withTimeout(
fetch("https://api.example.com/data").then(r => r.json()),
5000
)
.then(data => console.log(data))
.catch(error => console.error(error));
Sequential Execution
function sequential(tasks) {
return tasks.reduce(
(promise, task) => promise.then(task),
Promise.resolve()
);
}
sequential([
() => {
console.log("Task 1");
return new Promise(resolve => setTimeout(resolve, 1000));
},
() => {
console.log("Task 2");
return new Promise(resolve => setTimeout(resolve, 1000));
},
() => {
console.log("Task 3");
return Promise.resolve();
}
])
.then(() => console.log("All tasks complete"));
Parallel Execution
function parallel(tasks) {
return Promise.all(tasks.map(task => task()));
}
parallel([
() => new Promise(resolve => setTimeout(() => resolve("Result 1"), 1000)),
() => new Promise(resolve => setTimeout(() => resolve("Result 2"), 500)),
() => new Promise(resolve => setTimeout(() => resolve("Result 3"), 1500))
])
.then(results => console.log(results));
Converting Callbacks to Promises
// Callback-based function
function readFile(filename, callback) {
setTimeout(() => {
if (filename) {
callback(null, "File contents");
} else {
callback(new Error("Filename required"));
}
}, 1000);
}
// Convert to Promise
function readFilePromise(filename) {
return new Promise((resolve, reject) => {
readFile(filename, (error, data) => {
if (error) {
reject(error);
} else {
resolve(data);
}
});
});
}
readFilePromise("file.txt")
.then(data => console.log(data))
.catch(error => console.error(error));
Summary
- Promise: represents eventual completion of async operation
- States: pending, fulfilled, rejected
- then(): handle success
- catch(): handle error
- finally(): always execute
- Chaining: return promises to chain operations
- Promise.all(): wait for all promises
- Promise.race(): return first promise
- Promise.allSettled(): wait for all to settle
- Promise.any(): return first fulfilled
Related Resources
Official Documentation
Next Steps
- Async/Await: Modern Asynchronous Programming
- Fetch API: Making HTTP Requests
- Error Handling with Promises and Async/Await
Comments