<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Callback Hell Demo</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 900px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
.demo-section {
background-color: white;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
h2 {
color: #333;
margin-top: 0;
}
.code-block {
background-color: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 4px;
padding: 15px;
font-family: 'Courier New', monospace;
font-size: 14px;
overflow-x: auto;
}
button {
background-color: #007bff;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
margin-right: 10px;
margin-bottom: 10px;
}
button:hover {
background-color: #0056b3;
}
.output {
background-color: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 4px;
padding: 15px;
margin-top: 15px;
min-height: 100px;
white-space: pre-wrap;
}
.loading {
color: #666;
font-style: italic;
}
.error {
color: #dc3545;
}
.success {
color: #28a745;
}
.comparison {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-top: 20px;
}
@media (max-width: 768px) {
.comparison {
grid-template-columns: 1fr;
}
}
.comparison-box {
background-color: #f8f9fa;
padding: 15px;
border-radius: 4px;
border: 2px solid #dee2e6;
}
.comparison-box h3 {
margin-top: 0;
color: #495057;
}
.bad {
border-color: #dc3545;
}
.good {
border-color: #28a745;
}
</style>
</head>
<body>
<h1>Understanding Callback Hell</h1>
<div class="demo-section">
<h2>Scenario: Loading User Dashboard</h2>
<p>We need to:</p>
<ol>
<li>Get user ID from authentication</li>
<li>Fetch user details using that ID</li>
<li>Get user's posts</li>
<li>Get comments for the latest post</li>
<li>Get likes for that post</li>
</ol>
<button onclick="loadWithCallbackHell()">Load with Callback Hell 😱</button>
<button onclick="loadWithPromises()">Load with Promises 👍</button>
<button onclick="loadWithAsyncAwait()">Load with Async/Await 🌟</button>
<div id="output" class="output"></div>
</div>
<div class="demo-section">
<h2>Code Comparison</h2>
<div class="comparison">
<div class="comparison-box bad">
<h3>❌ Callback Hell</h3>
<pre class="code-block">
getUserId(function(userId) {
getUserDetails(userId, function(user) {
getUserPosts(user.id, function(posts) {
getPostComments(posts[0].id, function(comments) {
getPostLikes(posts[0].id, function(likes) {
// Finally display everything
displayDashboard(user, posts, comments, likes);
}, function(error) {
handleError(error);
});
}, function(error) {
handleError(error);
});
}, function(error) {
handleError(error);
});
}, function(error) {
handleError(error);
});
}, function(error) {
handleError(error);
});
</pre>
</div>
<div class="comparison-box good">
<h3>✅ Async/Await</h3>
<pre class="code-block">
try {
const userId = await getUserId();
const user = await getUserDetails(userId);
const posts = await getUserPosts(user.id);
const comments = await getPostComments(posts[0].id);
const likes = await getPostLikes(posts[0].id);
displayDashboard(user, posts, comments, likes);
} catch (error) {
handleError(error);
}
</pre>
</div>
</div>
</div>
<div class="demo-section">
<h2>Problems with Callback Hell</h2>
<ul>
<li><strong>Poor Readability:</strong> Code flows right and down instead of top to bottom</li>
<li><strong>Error Handling:</strong> Need to handle errors at every level</li>
<li><strong>Debugging Difficulty:</strong> Hard to set breakpoints and trace execution</li>
<li><strong>Code Reusability:</strong> Difficult to reuse or refactor nested callbacks</li>
<li><strong>Maintenance:</strong> Adding new steps or changing order is painful</li>
</ul>
</div>
<script>
const output = document.getElementById('output');
// Simulate async operations with delays
function simulateAsync(data, delay, callback, errorCallback) {
output.innerHTML += `<span class="loading">Loading ${data.type}...</span>\n`;
setTimeout(() => {
// Simulate occasional errors
if (Math.random() > 0.9) {
errorCallback(`Failed to load ${data.type}`);
} else {
callback(data.result);
}
}, delay);
}
// CALLBACK HELL EXAMPLE
function loadWithCallbackHell() {
output.innerHTML = '<h3>Loading with Callback Hell...</h3>\n';
// Get User ID
simulateAsync(
{ type: 'User ID', result: { id: 12345 } },
1000,
function (result) {
output.innerHTML += `<span class="success">✓ Got User ID: ${result.id}</span>\n`;
// Get User Details
simulateAsync(
{ type: 'User Details', result: { id: result.id, name: 'John Doe', email: 'john@example.com' } },
800,
function (user) {
output.innerHTML += `<span class="success">✓ Got User: ${user.name}</span>\n`;
// Get User Posts
simulateAsync(
{
type: 'User Posts', result: [
{ id: 1, title: 'My First Post', date: '2024-01-15' },
{ id: 2, title: 'Another Post', date: '2024-01-20' }
]
},
600,
function (posts) {
output.innerHTML += `<span class="success">✓ Got ${posts.length} posts</span>\n`;
// Get Comments for latest post
simulateAsync(
{
type: 'Post Comments', result: [
{ author: 'Alice', text: 'Great post!' },
{ author: 'Bob', text: 'Thanks for sharing' }
]
},
700,
function (comments) {
output.innerHTML += `<span class="success">✓ Got ${comments.length} comments</span>\n`;
// Get Likes for post
simulateAsync(
{ type: 'Post Likes', result: { count: 42, users: ['Alice', 'Bob', 'Charlie'] } },
500,
function (likes) {
output.innerHTML += `<span class="success">✓ Got ${likes.count} likes</span>\n`;
output.innerHTML += '\n<strong>Dashboard Loaded Successfully!</strong>\n';
output.innerHTML += `User: ${user.name}\nPosts: ${posts.length}\nLatest Post: "${posts[0].title}"\nComments: ${comments.length}\nLikes: ${likes.count}`;
},
function (error) {
output.innerHTML += `<span class="error">✗ ${error}</span>\n`;
}
);
},
function (error) {
output.innerHTML += `<span class="error">✗ ${error}</span>\n`;
}
);
},
function (error) {
output.innerHTML += `<span class="error">✗ ${error}</span>\n`;
}
);
},
function (error) {
output.innerHTML += `<span class="error">✗ ${error}</span>\n`;
}
);
},
function (error) {
output.innerHTML += `<span class="error">✗ ${error}</span>\n`;
}
);
}
// PROMISE-BASED SOLUTION
function simulateAsyncPromise(data, delay) {
output.innerHTML += `<span class="loading">Loading ${data.type}...</span>\n`;
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.9) {
reject(`Failed to load ${data.type}`);
} else {
resolve(data.result);
}
}, delay);
});
}
function loadWithPromises() {
output.innerHTML = '<h3>Loading with Promises...</h3>\n';
simulateAsyncPromise({ type: 'User ID', result: { id: 12345 } }, 1000)
.then(result => {
output.innerHTML += `<span class="success">✓ Got User ID: ${result.id}</span>\n`;
return simulateAsyncPromise({
type: 'User Details',
result: { id: result.id, name: 'John Doe', email: 'john@example.com' }
}, 800);
})
.then(user => {
output.innerHTML += `<span class="success">✓ Got User: ${user.name}</span>\n`;
window.tempUser = user; // Store for later use
return simulateAsyncPromise({
type: 'User Posts',
result: [
{ id: 1, title: 'My First Post', date: '2024-01-15' },
{ id: 2, title: 'Another Post', date: '2024-01-20' }
]
}, 600);
})
.then(posts => {
output.innerHTML += `<span class="success">✓ Got ${posts.length} posts</span>\n`;
window.tempPosts = posts;
return simulateAsyncPromise({
type: 'Post Comments',
result: [
{ author: 'Alice', text: 'Great post!' },
{ author: 'Bob', text: 'Thanks for sharing' }
]
}, 700);
})
.then(comments => {
output.innerHTML += `<span class="success">✓ Got ${comments.length} comments</span>\n`;
window.tempComments = comments;
return simulateAsyncPromise({
type: 'Post Likes',
result: { count: 42, users: ['Alice', 'Bob', 'Charlie'] }
}, 500);
})
.then(likes => {
output.innerHTML += `<span class="success">✓ Got ${likes.count} likes</span>\n`;
output.innerHTML += '\n<strong>Dashboard Loaded Successfully!</strong>\n';
output.innerHTML += `User: ${window.tempUser.name}\nPosts: ${window.tempPosts.length}\nLatest Post: "${window.tempPosts[0].title}"\nComments: ${window.tempComments.length}\nLikes: ${likes.count}`;
})
.catch(error => {
output.innerHTML += `<span class="error">✗ ${error}</span>\n`;
});
}
// ASYNC/AWAIT SOLUTION
async function loadWithAsyncAwait() {
output.innerHTML = '<h3>Loading with Async/Await...</h3>\n';
try {
const userId = await simulateAsyncPromise({ type: 'User ID', result: { id: 12345 } }, 1000);
output.innerHTML += `<span class="success">✓ Got User ID: ${userId.id}</span>\n`;
const user = await simulateAsyncPromise({
type: 'User Details',
result: { id: userId.id, name: 'John Doe', email: 'john@example.com' }
}, 800);
output.innerHTML += `<span class="success">✓ Got User: ${user.name}</span>\n`;
const posts = await simulateAsyncPromise({
type: 'User Posts',
result: [
{ id: 1, title: 'My First Post', date: '2024-01-15' },
{ id: 2, title: 'Another Post', date: '2024-01-20' }
]
}, 600);
output.innerHTML += `<span class="success">✓ Got ${posts.length} posts</span>\n`;
const comments = await simulateAsyncPromise({
type: 'Post Comments',
result: [
{ author: 'Alice', text: 'Great post!' },
{ author: 'Bob', text: 'Thanks for sharing' }
]
}, 700);
output.innerHTML += `<span class="success">✓ Got ${comments.length} comments</span>\n`;
const likes = await simulateAsyncPromise({
type: 'Post Likes',
result: { count: 42, users: ['Alice', 'Bob', 'Charlie'] }
}, 500);
output.innerHTML += `<span class="success">✓ Got ${likes.count} likes</span>\n`;
output.innerHTML += '\n<strong>Dashboard Loaded Successfully!</strong>\n';
output.innerHTML += `User: ${user.name}\nPosts: ${posts.length}\nLatest Post: "${posts[0].title}"\nComments: ${comments.length}\nLikes: ${likes.count}`;
} catch (error) {
output.innerHTML += `<span class="error">✗ ${error}</span>\n`;
}
}
</script>
</body>
</html>
No comments:
Post a Comment