Let’s review a few phases in event Loop for reference to my previous articleLibuv overview

  • Timers: This phase checks whether the timer expires and executes it
  • Poll (I/O): poll(I/O): this poll is used to listen for FD events, such as socket readability, writability, file readability, etc
  • Check: The callback of check Handle is called immediately after the event loop is finished being blocked by I/O. This is essentially the opposite of the prepare phase(the prepare callback is called right before the loop will block for I/O).

Why the emphasis on these stages? See thisThe node official website explains the Event loop

  1. The node timer setTimeout is executed in the timers phase to check whether the timer expires
  2. Node’s setImmediate is performed during the Check phase
  3. Others listen for file descriptors while cb is performed in the poll(I/O) phase

In fact, we can always solve the js code execution time what?

  1. The code before the Event loop starts
  2. The code executed by each phase of the Event loop means that the JS code of node will execute in two ways (purely personal opinion).

The next step is to parse their order of execution

  1. setTimeout(n) VS setImmediate

    SetImmediate mediate does not perform the check phase of the event loop. If the check phase is missed, setImmediate does not perform the check phase of the event loop.


    see
    article), see the figure below

    ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ┌ ─ > │ timers │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ │ I/O callbacks │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ │ idle, Prepare │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ incoming: │ │ │ poll │ < ─ ─ ─ ─ ─ ┤ connections, │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ data, Etc. │ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ │ check │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ├ ──┤ close callbacks ────── our r companyCopy the code

The timers phase precedes the Check phase in an event loop, suggesting that setTimeout is used first if the timer expires and setImmediate mediate mediate is displayed. If it is not in an Event loop, it is hard to say, and it needs to be analyzed in a case-by-case manner. So what is the sequence between setTimeout(n) and setImmediate?

  1. The implementation of process.nextTick is based on V8 MicroTask(a queue that would execute if there was no executable code in the current JS Call stack, lower than the JS Call stack code, but higher than the event loop, even in browsersCan see) mechanism. Does not belong to event loop(v8 MicroTask is integrated with Node)

    However, it is clear that nextTick uses V8’s MircoTasks mechanism and is called before the event loop continues after the current JS calk stack ends, which means that writing a recursive nextTick call will block the entire node
    setImmediate(() => {
      console.log('immediate');
    });
    function a() {
      process.nextTick(() => {
        console.log('set nextTick');
        a();
      });
    }
    a();Copy the code

    Try the code above and you’ll see that this is an infinite call. Also known as promises is v8 microTask (and promises use promises implemented in V8). Similarly, the callback of promise’s THEN and the callback in nextTick are determined by v8 MicroTask and are independent of event loop. This is also described in the promiseA+ specification. To test this, we can execute the following code

    const promise = Promise.resolve(234)
    setImmediate(() => {
      console.log('immediate');
    });
    testPromise();
    function testPromise() {
      promise = promise.then(() => {
        console.log('promise');
        testPromise();
      });
    }Copy the code

    Yes, the result is the same as pross. NextTick. The event loop is blocked.

    We can also see it in the source code

    The main code in/lib/internal/procss/next_tick js (there are so many logic didn’t understand only know a probably)tick queue

    The nextTick we create is managed by this global NextTickQueue, and when we execute nextTick, we push in a TickObject

    tick object

    The logic for executing nextTick is as follows

    nextTick

    The next step is to trigger the tickObject in the nextTickQueue

    tick queue handle

    The next step is to set the timing of the _tickCallback callback (_tickDomainCallback is the version that uses Domain)

    setUpNextTick

    The only thing that didn’t work was that I couldn’t find the _tickCallback time setting in the C++ code, and there was so much logic in it that I didn’t know what I was doing.

  2. SetImmediate is based on Libuv’s Event Loop.

    So now I know that nextTick must be executed first

To summarize

  1. The nextTick mediate command is not an Event loop and is not a part of v8’s Micro Tasks
  2. SetImmediate, setTimeout is an event loop, but the stages are different.
  3. NextTick’s promise’s callback is executed before the Event loop continues, meaning that their call will block the event loop. That is, be careful to block the Event loop when writing recursive calls or large loops using nextTick and Promise
  4. The setImmediate, setTimeout setting does not set itself again in this loop
  5. In the browser I guess setTimeout and promise, as well as MicroTasks

One last bit of code analysis for setTimeout and setImmediate, to explain exactly why 4 holds

reference

  1. task, micro tasks
  2. node source code
  3. event loop