Chaining Promises for Sequential Asynchronous Tasks
The then() method of a Promise returns a new Promise, enabling you to connect multiple asynchronous operations without deeply nested callbacks. What ever you return in side a then() callback determines the state and result of the subsequent Promise in the chain.
The first example uses setTimeout to mock network requests and demonstrates how chaining works in practice.
function fetchProvince() {
return new Promise((resolve) => {
setTimeout(() => resolve('Guangdong'), 1000);
});
}
const provincePromise = fetchProvince();
const cityPromise = provincePromise.then((province) => {
console.log('Province:', province);
return new Promise((resolve) => {
setTimeout(() => resolve(`${province} - Shenzhen`), 1000);
});
});
cityPromise.then((result) => {
console.log('City:', result);
});
console.log('Are the promises identical?', provincePromise === cityPromise); // false
The second example shows how Promise chaining replaces callback hell when fetching hierarchical data. Using the fetch API, it loads a list of provinces, then cities for the first province, and finally districts for the first city, updating a dropdown at each step.
let selectedProvince;
fetch('/api/province')
.then(response => response.json())
.then(data => {
selectedProvince = data[0].name;
document.getElementById('province-selector').textContent = selectedProvince;
return fetch(`/api/city?province=${selectedProvince}`);
})
.then(response => response.json())
.then(data => {
const selectedCity = data[0].name;
document.getElementById('city-selector').textContent = selectedCity;
return fetch(`/api/district?province=${selectedProvince}&city=${selectedCity}`);
})
.then(response => response.json())
.then(data => {
document.getElementById('district-selector').textContent = data[0].name;
})
.catch(error => console.error('Request failed:', error));