javascript

exercises

exercises.js
/**
 * ============================================
 * 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...');
Exercises - JavaScript Tutorial | DeepML