JavaScript Promises
Creating Promises
const delay = (ms) => new Promise((resolve) => {
setTimeout(() => resolve(`waited ${ms}ms`), ms);
});
const fetchData = (url) => new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = () => resolve(xhr.responseText);
xhr.onerror = () => reject(new Error(`Failed to fetch ${url}`));
xhr.send();
});
A Promise is a container for a future value. resolve signals success, reject signals failure. The executor function runs immediately.
Consuming Promises
delay(1000)
.then(msg => {
console.log(msg); // "waited 1000ms"
return "next value";
})
.then(val => console.log(val)) // "next value"
.catch(err => console.error(err))
.finally(() => console.log("done"));
.then() chains transformations. Each .then() returns a new Promise, enabling chaining. .catch() handles any error in the chain. .finally() runs regardless of success or failure.
Promise Combinators
const urls = [
"https://api.example.com/hosts",
"https://api.example.com/ports",
"https://api.example.com/vlans",
];
// Promise.all — wait for ALL, fail on first error
const results = await Promise.all(urls.map(u => fetch(u)));
// All succeeded — results is an array of responses
// Promise.allSettled — wait for ALL, never reject
const outcomes = await Promise.allSettled(urls.map(u => fetch(u)));
outcomes.forEach(({ status, value, reason }) => {
if (status === "fulfilled") console.log("OK:", value.status);
else console.error("FAIL:", reason);
});
// Promise.race — first to settle (resolve OR reject)
const fastest = await Promise.race([
fetch("https://api1.example.com/data"),
fetch("https://api2.example.com/data"),
]);
// Promise.any — first to RESOLVE (ignores rejections)
const first = await Promise.any([
fetch("https://primary.example.com"),
fetch("https://fallback.example.com"),
]);
Promise.all fails fast on the first rejection. Promise.allSettled gives you the result of every promise regardless. Choose based on whether partial failure is acceptable.
Error Handling
fetch("/api/hosts")
.then(res => {
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
})
.then(data => processData(data))
.catch(err => {
// catches network errors, HTTP errors, and processData errors
console.error("Pipeline failed:", err.message);
});
A .catch() at the end of a chain handles errors from any step above it. Unhandled promise rejections used to be silent — modern Node.js and browsers treat them as crashes.
Creating Resolved/Rejected Promises
// Already resolved
const cached = Promise.resolve({ name: "sw01", ip: "10.50.1.10" });
// Already rejected
const failed = Promise.reject(new Error("not implemented"));
// Useful for consistent return types
function getHost(name) {
const cache = hosts.get(name);
if (cache) return Promise.resolve(cache);
return fetchHost(name); // returns a Promise
}
Promise.resolve and Promise.reject wrap synchronous values in a Promise. This lets you return a consistent type from functions that may or may not need async work.