skip to Main Content

This script is from a Nodejs introducing book. This part is about the event loop of Javascript.

const sleep_st = (t) => new Promise((r) => setTimeout(r, t));
const sleep_im = () => new Promise((r) => setImmediate(r));

(async () => {
    setImmediate(() => console.log(1));
    console.log(2);
    await sleep_st(0);
    setImmediate(() => console.log(3));
    console.log(4);
})();

The two possible outputs are "2 4 1 3" and "2 1 4 3".

I expect the outputs are always consistent.

2

Answers


  1. The code produces a different order of numbers each time because of the interplay between the JavaScript event loop, microtasks, and macrotasks, which introduces non-deterministic behavior.

    Order of Execution

    • The event loop processes all microtasks before moving to the next macrotask.
    • The order in which setImmediate tasks are processed depends on the environment and when they are added relative to the current event loop tick.

    Step-by-Step Execution:

    1. Start Execution:

      • setImmediate(() => console.log(1)): Schedules a macrotask to log 1.
      • console.log(2): Logs 2 synchronously.
      • await sleep_st(0): Schedules a macrotask (setTimeout with 0 delay) and pauses the function until it resolves.
    2. Macrotask: sleep_st(0) Resolves:

      • After the macrotask completes, it resumes the async function.
    3. Inside the Async Function:

      • setImmediate(() => console.log(3)): Schedules another macrotask.
      • console.log(4): Logs 4 synchronously.
    4. Event Loop Execution:

      • The event loop processes the tasks in the following order:
        1. Remaining macrotasks (e.g., setImmediate callbacks).
        2. Microtasks scheduled during the previous phase.

    Why the Order Changes:

    1. setImmediate Timing:
      • The timing of setImmediate relative to setTimeout depends on the environment (Node.js handles them differently than browsers).
      • In some cases, setImmediate tasks can run before or after setTimeout with 0ms delay, depending on when they were scheduled.
    2. Race Condition:
      • setImmediate and setTimeout (even with 0ms) can race to execute their callbacks.
      • The exact timing of setImmediate(() => console.log(1)) and setImmediate(() => console.log(3)) introduces variability.
    Login or Signup to reply.
  2. Similar to sleep_st, you can use a promise wrapping setImmediate and await it. In this example the output is always 1 2 3 4. I faked setImmediate for the example since it doesn’t exist in browsers.

    const sleep_st = (t) => new Promise((r) => setTimeout(r, t));
    const sleep_im = () => new Promise((r) => setImmediate(r));
    
    const setImmediate = arg => setTimeout(arg, 0);
    
    const si = f => new Promise(r => setImmediate(() => {f();r();}));
    
    (async () => {
        await si(() => console.log(1));
        console.log(2);
        await sleep_st(0);
        await si(() => console.log(3));
        console.log(4);
    })();
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search