1.
The Callback Question
Task: Demonstrate the use of a callback function with an example.
Question: Create a simple Node.js example that simulates an asynchronous operation (like reading a
file or making a network request). Use a callback function to handle the result once the operation is
complete. Explain why this approach is essential for a non-blocking environment like Node.js.
const fs = require("fs");
fs.readFile("example.txt", "utf-8", (err, data) => {
if (err) {
console.error("Error reading file:", err);
return;
}
console.log("File contents:");
console.log(data);
});
console.log("This line runs before file reading completes!");
console.log("");
Example.txt
"Hello from example.txt
This file was read asynchronously!" | Out-File -Encoding utf8 example.txt
Output:
PS C:\Users\anits-csm\Desktop\csm 211 fs\FILE> node node-callback.js
This line runs before file reading completes!
File contents:
"Hello from example.txt
This file was read asynchronously!" | Out-File -Encoding utf8 example.txt
2. The Callback Hell & Promises Question
Task: Explain Callback Hell and resolve it using Promises.
Question: Explain what "Callback Hell" is and why it's a problem for code readability and
maintenance. Then, take the following example of nested callbacks and rewrite the entire flow using
Promises to make the code clean and sequential.
function getUser(id) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve({ id, name: "Kiran", email:
"kiran@example.com" }), 500);
});
}
function getOrders(userId) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve([{ id: 1, total: 50 }, { id: 2, total: 30
}]), 700);
});
}
function processOrders(orders) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve({ processedCount: orders.length }), 600);
});
}
function sendEmail(email, result) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(`Email sent to ${email} with
${result.processedCount} orders`), 400);
});
}
getUser(1)
.then(user => {
return getOrders(user.id).then(orders => ({ user, orders }));
})
.then(({ user, orders }) => {
return processOrders(orders).then(result => ({ user, result }));
})
.then(({ user, result }) => {
return sendEmail(user.email, result);
})
.then(info => {
console.log("All done:", info);
})
.catch(err => {
console.error("Error:", err);
});
Output:
PS C:\Users\anits-csm\Desktop\csm 211 fs\FILE> node promises.js
All done: Email sent to kiran@example.com with 2 orders
3. The async/await Question
Task: Demonstrate the need for async/await by refactoring Promise-based code.
Question: Using the Promise-based solution from the previous task, rewrite the code one more time
using async/await. Explain how async/await further simplifies asynchronous code, making it look
and feel more like synchronous code, and how it improves error handling.
function fetchData(success) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (success) {
resolve("Data fetched successfully!");
} else {
reject("Error fetching data!");
}
}, 1000);
});
}
async function getData() {
try {
const result1 = await fetchData(true);
console.log("Async/Await Result:", result1);
const result2 = await fetchData(false);
console.log("Async/Await Result 2:", result2);
} catch (error) {
console.error("Async/Await Error:", error);
}
}
getData();
Output:
PS C:\Users\anits-csm\Desktop\csm 211 fs\FILE> node async_wait.js
Async/Await Result: Data fetched successfully!
Async/Await Error: Error fetching data!