javascript
exercises
exercises.js⚡javascript
/**
* ============================================
* 6.4 THE EVENT LOOP - EXERCISES
* ============================================
*
* Practice predicting the order of async operations
* and understanding microtasks vs macrotasks.
*/
/**
* EXERCISE 1: Basic Order
* -----------------------
* Predict the output order.
*/
console.log('=== Exercise 1: Basic Order ===');
console.log('A');
setTimeout(() => console.log('B'), 0);
console.log('C');
// YOUR PREDICTION: ___
/*
* SOLUTION:
* A, C, B
*
* Sync code (A, C) runs first, then the timeout callback (B)
*/
/**
* EXERCISE 2: Promise vs Timeout
* ------------------------------
* Which runs first?
*/
console.log('\n=== Exercise 2: Promise vs Timeout ===');
setTimeout(() => console.log('Timeout'), 0);
Promise.resolve().then(() => console.log('Promise'));
// YOUR PREDICTION: ___
/*
* SOLUTION:
* Promise, Timeout
*
* Microtasks (Promise) run before macrotasks (setTimeout)
*/
/**
* EXERCISE 3: Multiple Callbacks
* ------------------------------
* Number the outputs 1-6.
*/
console.log('\n=== Exercise 3: Multiple Callbacks ===');
console.log('Start'); // Order: ___
setTimeout(() => {
console.log('Timeout 1'); // Order: ___
}, 0);
Promise.resolve()
.then(() => console.log('Promise 1')) // Order: ___
.then(() => console.log('Promise 2')); // Order: ___
setTimeout(() => {
console.log('Timeout 2'); // Order: ___
}, 0);
console.log('End'); // Order: ___
// YOUR ORDER: 1. ___, 2. ___, 3. ___, 4. ___, 5. ___, 6. ___
/*
* SOLUTION:
* 1. Start
* 2. End
* 3. Promise 1
* 4. Promise 2
* 5. Timeout 1
* 6. Timeout 2
*/
/**
* EXERCISE 4: Nested Timeout
* --------------------------
* When does each log appear?
*/
console.log('\n=== Exercise 4: Nested Timeout ===');
console.log('1');
setTimeout(() => {
console.log('2');
setTimeout(() => {
console.log('3');
}, 0);
}, 0);
console.log('4');
// YOUR PREDICTION: ___
/*
* SOLUTION:
* 1, 4, 2, 3
*
* Sync: 1, 4
* First timeout callback: 2 (also schedules inner timeout)
* Inner timeout callback: 3
*/
/**
* EXERCISE 5: Nested Promise
* --------------------------
* Trace the microtask queue.
*/
console.log('\n=== Exercise 5: Nested Promise ===');
Promise.resolve()
.then(() => {
console.log('A');
return Promise.resolve();
})
.then(() => {
console.log('B');
});
Promise.resolve().then(() => {
console.log('C');
});
// YOUR PREDICTION: ___
/*
* SOLUTION:
* A, C, B
*
* First microtask round: A, C (both initial .then)
* Second microtask round: B (returned promise resolves)
*/
/**
* EXERCISE 6: Complex Mix
* -----------------------
* This is tricky - trace carefully!
*/
console.log('\n=== Exercise 6: Complex Mix ===');
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => {
console.log('3');
setTimeout(() => console.log('4'), 0);
});
Promise.resolve().then(() => console.log('5'));
console.log('6');
// YOUR PREDICTION: ___
/*
* SOLUTION:
* 1, 6, 3, 5, 2, 4
*
* Sync: 1, 6
* Microtasks: 3, 5 (3 also schedules timeout for 4)
* Macrotasks: 2, 4 (2 was scheduled first)
*/
/**
* EXERCISE 7: async/await Order
* -----------------------------
* How does await affect execution order?
*/
console.log('\n=== Exercise 7: async/await ===');
async function test() {
console.log('A');
await Promise.resolve();
console.log('B');
}
console.log('1');
test();
console.log('2');
// YOUR PREDICTION: ___
/*
* SOLUTION:
* 1, A, 2, B
*
* 1: sync
* A: sync (before await)
* 2: sync (test() awaits, control returns)
* B: microtask (after await completes)
*/
/**
* EXERCISE 8: Double async
* ------------------------
* Two async functions interleaving.
*/
console.log('\n=== Exercise 8: Double async ===');
async function first() {
console.log('First start');
await Promise.resolve();
console.log('First end');
}
async function second() {
console.log('Second start');
await Promise.resolve();
console.log('Second end');
}
first();
second();
console.log('Sync');
// YOUR PREDICTION: ___
/*
* SOLUTION:
* First start, Second start, Sync, First end, Second end
*
* Both sync parts run, then both awaited parts as microtasks
*/
/**
* EXERCISE 9: Find the Bug
* ------------------------
* Why doesn't this work as expected?
*/
console.log('\n=== Exercise 9: Find the Bug ===');
let value = 'initial';
setTimeout(() => {
value = 'updated';
}, 0);
console.log('Value is:', value);
// EXPECTED: "updated"
// ACTUAL: ___
// WHY: ___
/*
* SOLUTION:
* ACTUAL: "initial"
* WHY: console.log runs synchronously, before the timeout callback
*
* FIX: Use the value inside the callback or use async/await
*/
/**
* EXERCISE 10: Fix the Bug
* ------------------------
* Rewrite to log "updated".
*/
console.log('\n=== Exercise 10: Fix the Bug ===');
// YOUR CODE:
// Approach 1: Callback
setTimeout(() => {
let value = 'updated';
console.log('Fixed value is:', value);
}, 0);
// Approach 2: Promise
/*
* SOLUTION:
*/
const getValue = () =>
new Promise((resolve) => {
setTimeout(() => resolve('updated'), 10);
});
getValue().then((v) => console.log('Promise fixed value:', v));
/**
* EXERCISE 11: queueMicrotask
* ---------------------------
* Where does queueMicrotask fit?
*/
console.log('\n=== Exercise 11: queueMicrotask ===');
console.log('1');
setTimeout(() => console.log('2'), 0);
queueMicrotask(() => console.log('3'));
Promise.resolve().then(() => console.log('4'));
queueMicrotask(() => console.log('5'));
console.log('6');
// YOUR PREDICTION: ___
/*
* SOLUTION:
* 1, 6, 3, 4, 5, 2
*
* queueMicrotask and Promise.then are both microtasks
* They run in the order they were queued
*/
/**
* EXERCISE 12: Why Zero Delay?
* ----------------------------
* Explain why setTimeout(fn, 0) is useful.
*/
console.log('\n=== Exercise 12: Zero Delay Purpose ===');
// Scenario: Update UI, then do heavy work
console.log('Show loading...');
setTimeout(() => {
console.log('Heavy work happening...');
// In browser, this lets the "loading" render first
}, 0);
console.log('UI updated');
// QUESTION: Why use setTimeout with 0ms delay?
// YOUR ANSWER: ___
/*
* SOLUTION:
* setTimeout(fn, 0) defers execution until after:
* 1. Current sync code completes
* 2. All microtasks are processed
* 3. (In browser) Rendering can happen
*
* This allows UI updates to appear before heavy work blocks the thread.
*/
/**
* EXERCISE 13: Starvation
* -----------------------
* Can microtasks starve macrotasks?
*/
console.log('\n=== Exercise 13: Starvation ===');
// WARNING: This could freeze your browser if uncommented!
//
// setTimeout(() => console.log("Will this ever run?"), 0);
//
// function recursiveMicrotask() {
// queueMicrotask(recursiveMicrotask);
// }
// recursiveMicrotask();
// QUESTION: Will the timeout ever execute?
// YOUR ANSWER: ___
/*
* SOLUTION:
* NO - the timeout will never run.
*
* Microtasks that queue more microtasks will run forever,
* preventing any macrotask from ever executing.
* This is called "microtask starvation."
*/
/**
* EXERCISE 14: Implement debounce
* -------------------------------
* Use setTimeout to implement debounce.
*/
console.log('\n=== Exercise 14: Debounce ===');
function debounce(fn, delay) {
// YOUR CODE HERE
// Return a function that:
// 1. Cancels any pending timeout
// 2. Schedules fn to run after delay
}
/*
* SOLUTION:
*/
function debounceSolution(fn, delay) {
let timeoutId = null;
return function (...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
const debouncedLog = debounceSolution(
(msg) => console.log('Debounced:', msg),
100
);
debouncedLog('a');
debouncedLog('b');
debouncedLog('c'); // Only this one logs after 100ms
/**
* EXERCISE 15: Promise Race Condition
* ------------------------------------
* Predict which resolves first.
*/
console.log('\n=== Exercise 15: Race Condition ===');
const fast = new Promise((resolve) => setTimeout(() => resolve('fast'), 10));
const slow = new Promise((resolve) => setTimeout(() => resolve('slow'), 50));
Promise.race([slow, fast]).then((winner) => {
console.log('Winner:', winner);
});
// YOUR PREDICTION: ___
/*
* SOLUTION:
* Winner: fast
*
* Promise.race resolves with the first promise to resolve,
* regardless of their order in the array.
*/
/**
* EXERCISE 16: Event Loop Visualization
* -------------------------------------
* Create a function that logs the event loop state.
*/
console.log('\n=== Exercise 16: Visualize Event Loop ===');
function visualizeLoop() {
// Log when sync, microtask, and macrotask code runs
// YOUR CODE HERE
}
/*
* SOLUTION:
*/
function visualizeLoopSolution() {
console.log('[SYNC] Script starts');
setTimeout(() => {
console.log('[MACRO] Timeout 1');
}, 0);
Promise.resolve().then(() => {
console.log('[MICRO] Promise 1');
});
queueMicrotask(() => {
console.log('[MICRO] queueMicrotask');
});
setTimeout(() => {
console.log('[MACRO] Timeout 2');
}, 0);
Promise.resolve().then(() => {
console.log('[MICRO] Promise 2');
});
console.log('[SYNC] Script ends');
}
visualizeLoopSolution();
console.log('\n=== Exercises Complete ===');
console.log('Wait for async operations to complete...');