本文主要讲解JavaScript的运行机制,对事件循环(Event Loop)进行深入研究。

JavaScript运行机制、事件循环

一、为什么js要是单线程

如果是多线程的话,有2个线程,同时对同一个DOM节点进行修改:一个要编辑该DOM节点的内容,一个要删除该DOM节点,那浏览器就不知道如何执行了。

二、为什么js要异步?

因为同步会阻塞js代码的执行,而异步不会阻塞。如果js不存在异步,那么js只能自上而下执行,当遇到某一处代码解析执行时间很长的时候,那么下面的代码将会被阻塞,对用户而言,阻塞意味着“卡死”,这样就导致很差的用户体验。

三、js如何实现异步?

通过事件循环(event loop)。

四、例子

<script>
    console.log(1);
    setTimeout(function() {  // 宏任务
        console.log(2)
    }, 100);
    setTimeout(function() {  // 宏任务
        console.log(3);
    }, 0);
    new Promise(function(resolve, reject) {  // 注意new Promise是同步任务(new关键字用来创建对象,属于同步任务)
        console.log(4);
        for (var i=0;i<10000;i++) {
            i == 99 && resolve();
        }
        console.log(5)
    }).then(function(){  // then是异步任务,而且是属于微任务
        console.log(6);
    }).catch(function() {
        console.log(7);
    });
    console.log(8);
</script>

分析:

运行上面代码会输出:1 4 5 8 6 3 2

  1. 遇到第一个宏任务<script>,第一行console.log(1)属于本轮宏任务,所以马上就执行,输出1。
  2. 遇到了一个setTimeout,是一个新的宏任务,就把这个setTimeout放到【宏任务队列】中,等一下一轮event loop再来执行。
  3. 又遇到了一个新的setTimeout,又是一个新的宏任务,同样,把这个setTimeout放到【宏任务队列】中,等待下一轮event loop再来执行。
  4. 遇到new Promise语句,属于本轮的宏任务,所以马上执行那个function中的内容,所以输出4 5。
  5. 遇到then,是一个微任务,于是将其加入到【微任务队列】中,等本轮的宏任务全部执行完了再来执行这个微任务。
  6. 然后遇到console.log(8),也属于本轮的宏任务,所以输出8。自此,本轮的宏任务全部执行完毕。
  7. 接下来检查本轮的【微任务队列】,发现有一个then的微任务,于是就执行then中的函数,所以输出7. 自此,本轮微任务也全部执行完,开始进入下一轮event loop
  8. 从这里开始,就是新的一轮event loop,开始执行一个新的宏任务,setTimeout,这里又2个setTimeout,而且等待的时间不同,那会先显示那个呢?答案是:当setTime等待的时间相同时,那么就按进入【队列】的顺序执行;如果等待的时间不同,那么就会先执行【等待时间短】的那个setTimeout的回调。(其实也很好理解,等待时间短的就说明会先完成等待,因此会先执行回调嘛)。所以回到上面的程序,一个setTimeout要等待的是100ms,而另一个到等待0ms,很明显,就是等待0ms的那个setTimeout的回调先回执行,所以先显示3,再显示2。

总结

除了广义的同步任务和异步任务,我们对任务有更精细的定义:

  • macro-task(宏任务):包括整体代码script,setTimeout,setInterval
  • micro-task(微任务):Promise,process.nextTick

规则:

  • <script>一般都是第一个执行的宏任务;

  • 在执行宏任务的过程中,若遇到一个新的宏任务,那么就会将这个新的宏任务放到【宏任务队列】中,等下一个event loop再来做;

  • 在执行宏任务的过程中,若遇到一个新的微任务,那么就会将这个新的微任务放到【微任务队列】中,等本轮宏任务做完后,再来处理这个微任务。

  • 做完本轮宏任务后,会去【微任务队列】中,检查是否有微任务,如果有则执行微任务,执行完后,就完成了本轮event loop,进入下一个event loop。

图片1

图片2

参考文章:

  1. 这一次,彻底弄懂 JavaScript 执行机制
  2. 宏任务和微任务:setTimeout和Promise执行顺序
  3. 10分钟理解JS引擎的执行机制