Files
learning-log/retrospective/week-01-retrospective.md
2026-01-10 21:18:01 +01:00

4.8 KiB

2025-12-21

Week 1 Retrospective

JavaScript is single threaded language. This means that only one task can run at a time. This is often referred to as the call stack. This means that some processes will freeze if some ongoing process will take time. I.E fetching data while rendering data in the DOM. This is not good for performance and for the experience of the end user.

Introducing the Event Loop, Microtasks & Macrotasks.

It wont make JS a multithreaded language but you can utilize some of the web-apis to delegate tasks to make it seem more multithreaded. This is cool.

An example is a classic for loop or for each. If heavy tasks are being run on the client while also telling the browser to paint in the DOM, it will freeze at some point.

Utilizing micro and macrotasks you will free up the call stack and let the DOM paint regularly while still keeping the web app smooth and functioning.

The order of which, things get run is.

  1. Call stack (Synchronous code)
  2. Microtasks (Also called the VIP queue)
  3. Macrotasks

The call stack is the code that gets run one by one. Variable assignments. Math. Painting to the dom etc.

Macrotasks are some of the web-apis. In this case it's setTimeout, setInterval and so on.

Microtasks is the VIP queue which gets first priority. In this case it's the Promise api.

If you were to write a huge for loop of promises, you would freeze the ui until it's done. That is not good when fetching streams of data. I.E AI response.

Here are some examples of code that freeze the AI and some that use genereator functions to yield the response. This is mock data.

  // 1. THE BAD WAY (Synchronous Blocking)
  // This simulates a heavy task running entirely on the Call Stack.
  function runHeavyTaskSync() {
    status = "Blocking the Main Thread...";

    // Simulate 3 seconds of heavy CPU work
    const start = Date.now();
    while (Date.now() - start < 3000) {
      // The CPU is stuck in this loop.
      // It CANNOT update the UI, run CSS animations, or handle clicks.
      count++;
    }

    status = "Done (Sync)!";
  }

  // 2. THE TRAP (Microtask Blocking)
  // Many devs think "Promises make it non-blocking." Let's test that lie.
  async function runMicrotaskTrap() {
    status = "Flooding Microtask Queue...";

    // Imagine we have 10,000 tiny tasks
    for (let i = 0; i < 10000; i++) {
      // Promise.resolve() puts the task in the VIP Queue (Microtask)
      await Promise.resolve();
      count++;
    }

    status = "Done (Microtask)!";
  }

  // 3. THE FIX (Macrotask Yielding)
  // We force the Event Loop to take a breath.
  async function runFixedTask() {
    status = "Running Smoothly (Yielding)...";

    for (let i = 0; i < 100; i++) {
        // Run a small chunk of work
        heavyMathChunk();

        // THE MAGIC: setTimeout(..., 0) puts this in the Macrotask Queue (Regular Line)
        // This lets the Browser paint the screen before coming back.
        await new Promise(resolve => setTimeout(resolve, 0));
    }

    status = "Done (Fixed)!";
  }

 // 1. THE BAD WAY (Synchronous Blocking)
  // This simulates a heavy task running entirely on the Call Stack.
  function runHeavyTaskSync() {
    status = "Blocking the Main Thread...";

    // Simulate 3 seconds of heavy CPU work
    const start = Date.now();
    while (Date.now() - start < 3000) {
      // The CPU is stuck in this loop.
      // It CANNOT update the UI, run CSS animations, or handle clicks.
      count++;
    }

    status = "Done (Sync)!";
  }

  // 2. THE TRAP (Microtask Blocking)
  // Many devs think "Promises make it non-blocking." Let's test that lie.
  async function runMicrotaskTrap() {
    status = "Flooding Microtask Queue...";

    // Imagine we have 10,000 tiny tasks
    for (let i = 0; i < 10000; i++) {
      // Promise.resolve() puts the task in the VIP Queue (Microtask)
      await Promise.resolve();
      count++;
    }

    status = "Done (Microtask)!";
  }

  // 3. THE FIX (Macrotask Yielding)
  // We force the Event Loop to take a breath.
  async function runFixedTask() {
    status = "Running Smoothly (Yielding)...";

    for (let i = 0; i < 100; i++) {
        // Run a small chunk of work
        heavyMathChunk();

        // THE MAGIC: setTimeout(..., 0) puts this in the Macrotask Queue (Regular Line)
        // This lets the Browser paint the screen before coming back.
        await new Promise(resolve => setTimeout(resolve, 0));
    }

    status = "Done (Fixed)!";
  }

By using microtasks(Promise) & macrotasks(setTimout) combined in loops we can open the event loop and let the browser 'breathe'.

This is the same as using the generator functions.

I have written about generators here['../concepts/02-generators.md']

I have been using Svelte this week, which i do not have a lot of experience with. I have learned a lot of the syntax and next week will be about the differences about React & Svelte.