js事件循环由一题说开去....

292 阅读3分钟

夜仿佛纸浸了油变成半透明体;它给太阳拥抱住了,分不出身来,也许是给太阳陶醉了,所以夕照晚霞褪后的夜色也带着酡红。 ---《围城》

老规矩,上猫。

我想,学习js的第一课,老师大概就会一遍遍强调:js是单线程的,js是单线程的,js是单线程的。

单线程:说的通俗一点,就是代码在执行过程中,另一段代码想要执行就必须等当前代码执行完成后才可以进行。

假设,JS是多线程的。试想一种情况,多个线程同时修改一个DOM,哪个线程又不是正经线程呢,这时候,浏览器该如何选择?所以,这个时候,就会牵扯到一个问题,既然他是单线程的,有些事件可能会循环多次或者延迟执行,遇到这些事件的时候,总不能就一直在那儿等着。

  • JS代码执行过程中,同步任务直接执行,同步任务执行接受之后进行异步任务的执行。
  • 异步任务分类为微任务宏任务
  • 宏任务:由宿主环境提供的任务---setTimeout/setInterval
  • 微任务:由语言标准提供的任务----Promise/process.nextTick

事件循环:异步任务中,先执行微任务,再执行宏任务,在执行宏任务时,有可能产生新的任务,所以,宏任务执行结束后,会再去执行微任务,直到所有的异步任务执行结束。

废话不多说,直接带你一道题全面理解。

(function(){
    setTimeout(() => {
        console.log(0);
      });
      new Promise(resolve => {
        console.log(1);
        setTimeout(() => {
          resolve();
          Promise.resolve().then(() => console.log(2));
          console.log(3);
        });
        Promise.resolve().then(() => console.log(4));
      }).then(() => {
        console.log(5);
        Promise.resolve().then(() => console.log(6)); 
        setTimeout(() => console.log(7));
      });
      console.log(8);
})()

让我们来逐步解析这道题目

补充:

promise声明过程是同步的, then() 是异步的,只有在执行promise.resolve()后才会触发。

由此可见这道题中的任务5只有在任务3中的resolve()执行的时候才会触发。


--第一次循环--

同步 异步(微任务 异步(宏任务))
任务2 任务4 任务1
任务6 任务3

按照先同步后异步,先微任务后宏任务的顺序执行。

  • 任务2 --------> 打印1
  • 任务6---------> 打印8
  • 任务4 --------> 打印4

--第二次循环--

第二轮循环从宏任务队列开始

  • 任务1 --------> 打印0
  • 任务3 --------> 先将任务5放入微任务队列,再将任务7放入微任务队列,最后 打印3
同步 异步(微任务) 异步(宏任务)
任务5
任务7
  • 任务5 ------> 先打印5,将任务8扔进微任务,任务9扔进宏任务队列
  • 任务7 ------> 打印2

--第三次循环--

同步 异步(微任务) 异步(宏任务)
任务8 任务9
  • 任务8 ----> 打印6
  • 任务9 -----> 打印7

最终打印的结果为:1,8,4,0,3,5,2,6,7

我们可以看到,控制台的执行结果中,多了一个“undefined”。这是因为,在第一轮循环结束后,控制台会输出自执行函数的执行结果,因为函数中没有设置返回值,默认返回"undefined""

以上

特别鸣谢:顾陇童小虎