start

There is a nextTick method in Vue, and one day I noticed that regardless of the order of the code, nextTick was always executed before setTimeout. Why do you have to queue faster than me?

The opening question, the content depends entirely on the compilation. (Run under Node, answer at the end of this article.)

new Promise((resolve) = > {
    console.log(1);
    
    process.nextTick((a)= > {
    	console.log(2);
    });
    
    resolve();
    
    process.nextTick((a)= > {
    	console.log(3);
    });
    
    console.log(4);
}).then((a)= > {
    console.log(5);
});

setTimeout((a)= > {
    console.log(6);
}, 0);

console.log(7);
Copy the code

So, what is the order of printing?

Event loop

for(var i = 0; i < 5; i++){
    setTimeout(function after() {
        console.log(i);
    }, 0);
}
Copy the code

This question must be seen a lot of, the answer blurts out 5 5. Why is that? A: Closures. Why do closures happen? Answer:…

All of these things to start from the goddess patching the sky (why don’t you start from Pan Gu opened the sky? .

A quick explanation:

  1. Js is normally executed from the top down and is placed in the Call Stack (see the Call Stack) as it executes.
  2. When an asynchronous event is executed (Ajax, timer, etc.), the browser creates a timer as part of the Web API that handles the countdown for you.
  3. When the time is up, a Callback Queue is entered.
  4. The event loop takes the function from the callback queue and pushes it onto the call stack.
  5. Start with step one.

So, even setTimeout(fn, 0) (actually the minimum interval is 4ms) is executed from the next event cycle.

In the previous example, since the after function refers to I and will be called in the next event cycle, the memory of I cannot be freed. The I’s have been cooked to a 5.

On memory, I’ve translated an article about how JavaScript works: Memory Management + How to handle 4 common memory leaks. It’s also very helpful to understand closures.

I have only briefly mentioned the event loop here, see the references at the end of this article for more details.

Macro and micro tasks

A host environment has only one event loop, but can have multiple task queues. Macro task queue and micro task queue are two of them.

During each event loop, tasks in the macro task queue are executed before tasks in the microtask queue are executed. So what are macro tasks and micro tasks?

  • Macro tasks: Script (global task), setTimeout, setInterval, setImmediate, I/O, UI Rendering.
  • Microtasks: Process. nextTick, Promise, Object.observer, MutationObserver.
new Promise((resolve) = > {
    resolve();
}).then((a)= > {
    console.log(1);
});
setTimeout((a)= > {
    console.log(2);
}, 0);
console.log(3);
Copy the code

According to the above statement, it should print 3, 2, 1. But it actually prints out 3, 1, 2. It turns out that microtasks like process.nextTick and Promise are added to the current loop microtask queue. So it will be executed later than all the macro tasks in the current loop and before the macro tasks in the next loop.

Process. NextTick and Promise

process.nextTick((a)= > {
    console.log(1); 
});
new Promise((resolve) = > {
    resolve();
}).then((a)= > {
    console.log(2);
});
process.nextTick((a)= > {
    console.log(3); 
});
Copy the code

Why do I mention these two microtasks? Test it out for yourself, because the results might surprise you. why?

Fortunately, the Internet is powerful. There is nothing baidu can’t do, if there is, then Google.

“Process. nextTick is always greater than promise.then for a simple reason… In Node, _tickCallback is called every time a task in the TaskQueue completes, and the _tickCallback essentially does two things:

  1. All tasks in nextTickQueue execute (maximum length: 1e4, Node version: v6.9.1)
  2. After the first step, the _runMicrotasks function is executed, executing the part of the microTask (the callback registered with promise.then) so it is clear that process.nexttick > Promise.then”

miss

NextTick in Vue is a mixture of macro tasks and micro tasks, which need to be manually switched. At last the truth came out. Timer: Well, I’ll forgive you for being ahead of me.

So what’s the answer to the opening question? Try it out for yourself.

Paper come zhongjue shallow, realize this to practice

Why, miss? Miss What? It is as you say

I: roll, dozen wrong just. Is the summary.

Me: What? You treat! Go on, go on!

Owner arrested, over.


setImmediate

There is another strange aspect of the order battle.

setImmediate((a)= > {
    console.log(1);
});

setTimeout((a)= > {
    console.log(2);
}, 0);
Copy the code

However, you’ll notice that sometimes the fuck prints 1, 2, and sometimes 2, 1. Why are you acting like a woman.

Nodejs explains:

  • SetImmediate (): Is designed to mediate once the current phase of the task has been performed.
  • SetTimeout (): Delay code execution.

If it is not executed in an I/O cycle, the order of execution is uncertain.

SetImmediate always takes precedence over setTimeout if executed during an I/O cycle.

const fs = require('fs');

fs.readFile(__filename, () => {
    setTimeout((a)= > {
        console.log('timeout');
    }, 0);
    setImmediate((a)= > {
        console.log('immediate');
    });
});
Copy the code

Always: Print immediate and then timeout.


References:

  1. Ruan Yifeng’s article — Detailed explanation of JavaScript operation mechanism: Talk about Event Loop again. (There are some errors in this article, I suggest you try each example yourself, read the comments, and check the information.)
  2. Node.js Event Loop, Timers, and process.nexttick ();
  3. The answer that nextTick takes precedence over promise is found in zhihu’s answer – how does the promise queue relate to the setTimeout queue? ;
  4. This article is part of a series on How javascript works: Event loop and the rise of Async programming + 5 ways to better coding with Async /await)
  5. JavaScript asynchrony, stacks, event loops, task queues