Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,21 @@ The Event Loop is a critical part of JavaScript's concurrency model, ensuring no

**[⬆ Back to Top](#table-of-contents)**

### Event Loop demo (interactive)

There is a small interactive demo included in this repository to visualize microtasks vs macrotasks ordering in the browser.

- File: `event-loop-demo.html`
- How to open: double-click the file in your file manager or open it in your browser. From the repo root you can also run a simple static server, for example with Python 3:

```powershell
# from repository root
python -m http.server 8080; # then open http://localhost:8080/event-loop-demo.html
```

The demo logs messages to the page and to the browser console showing the expected ordering between synchronous code, microtasks (Promises / async / MutationObserver) and macrotasks (setTimeout).


---

## 10. setTimeout, setInterval and requestAnimationFrame
Expand Down
195 changes: 195 additions & 0 deletions event-loop-demo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Event Loop: Message Queue and Microtasks Demo</title>
<style>
body {
font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
padding: 1rem;
max-width: 800px;
margin: 0 auto;
}
#output {
margin-top: 1rem;
border: 1px solid #ddd;
padding: 0.5rem;
background: #fafafa;
min-height: 200px;
}
.log {
padding: 0.25rem 0;
font-family: monospace;
display: flex;
align-items: center;
}
.note {
color: #555;
font-size: 0.9rem;
margin-bottom: 1rem;
}
.macrotask { color: #2c5282; }
.microtask { color: #805ad5; }
.sync { color: #2f855a; }
.timestamp {
color: #666;
font-size: 0.8em;
margin-right: 8px;
}
button {
padding: 8px 16px;
margin: 4px;
font-size: 14px;
cursor: pointer;
}
.controls {
margin: 1rem 0;
display: flex;
gap: 8px;
}
</style>
</head>
<body>
<h1>Event Loop: Message Queue and Microtasks Demo</h1>
<p class="note">This demo illustrates how the Event Loop processes tasks from the Message Queue (macrotasks) and Microtask Queue. The Message Queue handles setTimeout callbacks and DOM events, while the Microtask Queue processes Promises and async/await operations with higher priority.</p>

<div class="controls">
<button id="run">Run Basic Demo</button>
<button id="runAdvanced">Run Advanced Demo</button>
<button id="triggerEvent">Trigger DOM Event</button>
<button id="clear">Clear Output</button>
</div>

<div id="output" aria-live="polite"></div>

<!-- hidden element used to trigger MutationObserver -->
<div id="trigger" style="display:none"></div>

<script>
function log(message, type = 'sync') {
const out = document.getElementById('output');
const d = document.createElement('div');
d.className = `log ${type}`;

// Add timestamp
const timestamp = document.createElement('span');
timestamp.className = 'timestamp';
timestamp.textContent = new Date().toLocaleTimeString([], { hour12: false });

const content = document.createElement('span');
content.textContent = message;

d.appendChild(timestamp);
d.appendChild(content);
out.appendChild(d);
console.log(`${timestamp.textContent}: ${message}`);
}

function clear() {
document.getElementById('output').innerHTML = '';
}

// Basic demo showing core concepts
function runDemo() {
clear();
log('Script Start', 'sync');

setTimeout(() => {
log('setTimeout 0ms (macrotask)', 'macrotask');
}, 0);

Promise.resolve()
.then(() => {
log('Promise.then 1 (microtask)', 'microtask');
})
.then(() => {
log('Promise.then 2 (microtask)', 'microtask');
});

(async function asyncExample() {
log('Async Function Start', 'sync');
await null; // yields to microtask queue
log('After await (microtask)', 'microtask');
})();

// MutationObserver example (microtask)
const obs = new MutationObserver(() => {
log('MutationObserver callback (microtask)', 'microtask');
obs.disconnect();
});
obs.observe(document.getElementById('trigger'), { attributes: true });
document.getElementById('trigger').setAttribute('data-test', Date.now());

log('Script End', 'sync');
}

// Advanced demo with more complex scenarios
function runAdvancedDemo() {
clear();
log('Advanced Demo Start', 'sync');

// Multiple macrotasks with different delays
setTimeout(() => {
log('setTimeout 0ms (macrotask 1)', 'macrotask');

// Nested microtask
Promise.resolve().then(() => {
log('Nested Promise in setTimeout (microtask)', 'microtask');
});
}, 0);

setTimeout(() => {
log('setTimeout 2000ms (macrotask 2)', 'macrotask');
}, 2000);

// Chain of microtasks
Promise.resolve()
.then(() => {
log('Promise chain 1 (microtask)', 'microtask');
return Promise.resolve();
})
.then(() => {
log('Promise chain 2 (microtask)', 'microtask');
// Scheduling a macrotask from microtask
setTimeout(() => {
log('setTimeout from Promise chain (macrotask)', 'macrotask');
}, 0);
});

// Async/await example
(async function complexAsyncExample() {
log('Complex Async Start', 'sync');

await Promise.resolve();
log('After first await (microtask)', 'microtask');

await Promise.resolve();
log('After second await (microtask)', 'microtask');

setTimeout(() => {
log('setTimeout in async function (macrotask)', 'macrotask');
}, 1000);
})();

// Direct microtask scheduling
queueMicrotask(() => {
log('queueMicrotask callback (microtask)', 'microtask');
});

log('Advanced Demo End', 'sync');
}

// Event Handlers
document.getElementById('run').addEventListener('click', runDemo);
document.getElementById('runAdvanced').addEventListener('click', runAdvancedDemo);
document.getElementById('clear').addEventListener('click', clear);
document.getElementById('triggerEvent').addEventListener('click', () => {
log('DOM Event Handler (macrotask)', 'macrotask');
});

// Run basic demo on load
window.addEventListener('load', runDemo);
</script>
</body>
</html>