Certainly! Let's start with an example using callback hell and then refactor it step by step using Promises.
Example with Callback Hell:
// Assume we have three asynchronous operations: fetchData, process1, and process2
function fetchData(callback) {
setTimeout(() => {
console.log("Data fetched successfully!");
callback(null, { data: "some data" });
}, 1000);
}
function process1(data, callback) {
setTimeout(() => {
console.log("Processing step 1 completed!");
callback(null, data + " processed");
}, 1000);
}
function process2(data, callback) {
setTimeout(() => {
console.log("Processing step 2 completed!");
callback(null, data + " processed again");
}, 1000);
}
// Callback Hell
fetchData((err1, result1) => {
if (err1) {
console.error("Error fetching data:", err1);
return;
}
process1(result1.data, (err2, result2) => {
if (err2) {
console.error("Error processing step 1:", err2);
return;
}
process2(result2, (err3, result3) => {
if (err3) {
console.error("Error processing step 2:", err3);
return;
}
console.log("Final result:", result3);
});
});
});
This is a classic example of callback hell. Asynchronous operations are nested inside each other, making the code hard to read and maintain.
Refactoring with Promises:
Now, let's refactor the code step by step using Promises.
- Promisify
fetchData
:
function fetchDataPromise() {
return new Promise((resolve, reject) => {
fetchData((err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
}
Now, fetchDataPromise
returns a Promise that resolves with the data.
- Promisify
process1
:
function process1Promise(data) {
return new Promise((resolve, reject) => {
process1(data, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
}
- Promisify
process2
:
function process2Promise(data) {
return new Promise((resolve, reject) => {
process2(data, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
}
- Using Promises:
// Refactored code using Promises
fetchDataPromise()
.then((result1) => process1Promise(result1.data))
.then((result2) => process2Promise(result2))
.then((finalResult) => {
console.log("Final result:", finalResult);
})
.catch((error) => {
console.error("Error:", error);
});
By using Promises, the code becomes more readable and avoids the callback hell structure. Each asynchronous operation is wrapped in a Promise, and the .then
method is used to chain the operations together. The .catch
method is used to handle errors at any step in the chain.
ย