Promises
A Promise is an object that represents the result (success or failure) of a future asynchronous operation. A Promise has three states: pending (not yet finished), fulfilled (succeeded), and rejected (failed).
Code Example
Basic Promises
// Create a Promise
const fetchStudentData = new Promise((resolve, reject) => {
// Simulated asynchronous operation
setTimeout(() => {
const successful = true;
if (successful) {
resolve({
studentId: '2024001',
name: 'Mike Johnson',
major: 'Computer Science'
});
} else {
reject(new Error('Failed to fetch student data'));
}
}, 1000);
});
// Use the Promise
fetchStudentData
.then(data => {
console.log('Data:', data);
})
.catch(error => {
console.error('Error:', error.message);
});
then(), catch(), finally()
const processGrade = new Promise((resolve, reject) => {
setTimeout(() => {
const grade = Math.floor(Math.random() * 100);
resolve(grade);
}, 500);
});
processGrade
.then(grade => {
console.log('Grade:', grade);
return grade + 10; // the returned value becomes the input for the next then
})
.then(finalGrade => {
console.log('Final grade:', finalGrade);
})
.catch(error => {
console.error('Error:', error);
})
.finally(() => {
console.log('Processing complete'); // always executed
});
Code Explanation
Promise.all()
All Promises must resolve. If one fails, they all fail.
const p1 = fetch('/api/student').then(r => r.json());
const p2 = fetch('/api/lecturer').then(r => r.json());
const p3 = fetch('/api/course').then(r => r.json());
Promise.all([p1, p2, p3])
.then(([student, lecturer, course]) => {
console.log('Student:', student.length);
console.log('Lecturer:', lecturer.length);
console.log('Courses:', course.length);
})
.catch(error => {
console.error('One failed:', error);
});
Promise.allSettled()
All Promises run until completion. No rejection cancels the rest.
const tasks = [
Promise.resolve('Data A successful'),
Promise.reject('Data B failed'),
Promise.resolve('Data C successful')
];
Promise.allSettled(tasks)
.then(results => {
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Task ${index}: OK -`, result.value);
} else {
console.log(`Task ${index}: FAILED -`, result.reason);
}
});
});
// Output:
// Task 0: OK - Data A successful
// Task 1: FAILED - Data B failed
// Task 2: OK - Data C successful
Promise.race()
Whichever resolves/rejects first is the one that is taken.
const serverIndo = fetch('https://api-id.university.edu/data');
const serverSg = fetch('https://api-sg.university.edu/data');
Promise.race([serverIndo, serverSg])
.then(response => {
// The response from the faster server
console.log('Server tercepat:', response.url);
return response.json();
})
.catch(error => {
console.error('All servers timeout:', error);
});
Promise.any()
Take the one that resolves first. Only reject if all of them reject.
const mirror1 = fetch('https://mirror1.university.edu/data');
const mirror2 = fetch('https://mirror2.university.edu/data');
const mirror3 = fetch('https://mirror3.university.edu/data');
Promise.any([mirror1, mirror2, mirror3])
.then(response => {
// The response from the mirror that resolved first
console.log('Result from:', response.url);
return response.json();
})
.catch(error) {
// All mirrors failed
console.error('All mirrors failed:', error.errors);
}
Real-World Examples
Parallel Fetch of Student & Lecturer Data
async function loadDashboard() {
try {
const [studentRes, lecturerRes, statsRes] = await Promise.all([
fetch('/api/student?page=1&limit=10'),
fetch('/api/lecturer?page=1&limit=10'),
fetch('/api/statistics')
]);
const [student, lecturer, stats] = await Promise.all([
studentRes.json(),
lecturerRes.json(),
statsRes.json()
]);
renderStudentTable(student);
renderFacultyTable(lecturer);
renderStatistics(stats);
} catch (error) {
showError('Failed to load dashboard:', error);
}
}
Fallback with Promise.any
// Try several mirror servers
async function fetchDataWithFallback() {
const mirrors = [
'https://api1.university.edu/student',
'https://api2.university.edu/student',
'https://api3.university.edu/student'
];
const promises = mirrors.map(url =>
fetch(url).then(r => {
if (!r.ok) throw new Error(`Status ${r.status}`);
return r.json();
})
);
try {
const data = await Promise.any(promises);
return data;
} catch (error) {
console.error('All mirrors failed:', error.errors);
throw new Error('No server available');
}
}
Retry Mechanism
function fetchWithRetry(url, maxRetries = 3) {
return new Promise((resolve, reject) => {
let attempts = 0;
function attempt() {
attempts++;
fetch(url)
.then(response => {
if (!response.ok && attempts < maxRetries) {
setTimeout(attempt, 1000 * attempts); // exponential backoff
return;
}
resolve(response);
})
.catch(error => {
if (attempts < maxRetries) {
setTimeout(attempt, 1000 * attempts);
} else {
reject(error);
}
});
}
attempt();
});
}
Loading States with Promises
let loadingPromises = [];
function showLoading(promise, label = 'Memprocess...') {
const loadingEl = document.createElement('div');
loadingEl.className = 'loading-overlay';
loadingEl.innerHTML = `<div class="spinner"></div><p>${label}</p>`;
document.body.appendChild(loadingEl);
loadingPromises.push(
promise.finally(() => {
loadingEl.remove();
})
);
}
// Usage
const savePromise = fetch('/api/save', { method: 'POST', body: data });
showLoading(savePromise, 'Menyimpan data...');
savePromise.then(() => showNotification('Tersave!'));
Promises vs Callback Hell
// Callback hell — hard to read
fetchStudent(studentId, function (student) {
fetchGrade(student.studentId, function (grade) {
fetchSummary(grade, function (summary) {
console.log(summary);
}, handleError);
}, handleError);
}, handleError);
// Promises — clean and readable
fetchStudent(studentId)
.then(student => fetchGrade(student.studentId))
.then(grade => fetchSummary(grade))
.then(summary => console.log(summary))
.catch(handleError);
Conclusion
Promises turn callback-based code into more readable code. Use Promise.all() for parallel operations that must all succeed, Promise.allSettled() for independent parallel operations, Promise.race() for timeouts/fallbacks, and Promise.any() for redundant requests. Use .catch() and .finally() for error handling and cleanup.