哎一古,这个问题是面试的常客啊!让我来好好学习一下吧!!

# 为什么JavaScript是单线程?

单线程的意思就是说:一次只能干一件事儿。

那为什么要有这么没有效率的机制呢?来举个栗子🌰: 如果不是单线程,那就可以一下干好多事儿啦。一个线程当我们在给某个DOM节点增加内容的时候,另一个线程正在删除这个DOM节点的内容,那要听谁的呢?这就乱套了!所以还是老老实实单线程,一件一件的完成。(虽然javascript是单线程,但是javascript中有同步和异步的概念,解决了js阻塞的问题。)

# 同步和异步

同步:同步就是说,如果在一个函数返回的时候,调用者就能够即时得到预期结果
异步:异步就是说,如果在函数返回的时候,调用者还不能够得到预期结果,而是需要在将来通过一定的手段得到,像setTimeout()就是一个异步函数

JavaScript里面有两种任务:
同步任务:在主线程(执行栈)排队执行的任务,只有前一个执行完毕,才能执行下一个任务
异步任务:在任务队列(先入先出),只有任务队列通知主线程,可以执行某个异步任务的时候,该任务才会进入主线程。 "任务队列"是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。任务队列又分为宏任务和微任务

  • 宏任务:setTimeOut、setInterval、setImmediate、I/O、各种callback、UI渲染等
  • 微任务:then、process.nextTick 、Promise 、MutationObserver 、async(实质上也是promise)

# 事件循环

线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)

为了能更好地理解这个事件循环,自己做了个图💡 eventloop

PS:设计好难,找了半天配色还是好丑😭

执行流程
1️⃣ JavaScript内核加载代码到执行栈
2️⃣ 执行栈依次执行同步任务,发现调用异步API则会添加回调事件到回调(任务)队列中。
3️⃣ 宏任务进入宏任务队列,微任务进入微任务队列。
4️⃣ 栈内代码执行完毕,查看微任务队列是否为空,若不为空,微任务队列中的所有微任务进入执行栈执行(相当于清空队列)。
5️⃣ 执行栈执行完微任务,此时微任务队列为空,宏任务队列第一条宏任务(先进先出原则哦)进入执行栈。
6️⃣ 执行宏任务,若此过程总又再遇到微任务或者宏任务,则执行3️⃣。

由此可见,这样的执行流程就构成了一个事件的循环检测机制,即我们所称的eventloop。
PS:宏任务总会在下一个EventLoop中执行。

✨先来一道题看看是不是真的理解好了:

console.log(1);
setTimeout(function() {
    console.log(2);
}, 0);
new Promise(function(resolve) {
    console.log(3);
    resolve(Date.now());
}).then(function() {
    console.log(4);
});
console.log(5);
setTimeout(function() {
    new Promise(function(resolve) {
        console.log(6);
        resolve(Date.now());
    }).then(function() {
        console.log(7);
    });
}, 0);
//来猜猜执行结果🤠
Answer

1. 执行 console.log(1); 输出1
2. 遇到 第一个setTimeout() push到宏任务队列。
3. 执行 console.log(3) 输出3;遇到 .then() ,push到微任务队列。
4. 执行 console.log(5) 输出5
5. 遇到 第二个setTimeout() push到宏任务队列。
6. 同步任务代码执行完毕,执行栈为空。读取微任务队列。执行 .then() 输出4
7. 执行栈为空,微任务队列为空,宏任务队列顶部宏任务进栈。执行 第一个setTimeout() 输出2
8. 执行栈为空,微任务队列为空,执行下一个宏任务 第二个setTimeout() 输出6, 遇到 .then() push到微任务队列。
9. 执行栈为空,微任务队列不为空,执行微任务 .then() 输出7
💡 因此,最终的输出顺序为:1, 3, 5, 4, 2, 6, 7;

# 题解

Q: 我的setTimeout函数到时间了,为啥一直不去执行。

A: setTimeOut的回调会被放到任务队列中,需要当前的执行栈执行完了,才会去执行执行任务队列中的内容。出现setTimeout回调不及时,说明在执行栈中出现了阻塞,或者说执行代码过多。

Q: 微任务有两种 nextTick 和 then 那么这两个谁快呢?

A: nextTick快。