2020年了,学前端的不会还有人不知道浏览器的事件环吧

241 阅读5分钟

1.JS是单线程的

JS中的代码都是串行的, 前面没有执行完毕后面不能执行

2.执行顺序

  • 程序运行会从上至下依次执行所有的同步代码

  • 在执行的过程中如果遇到异步代码会将异步代码放到事件循环中

  • 当所有同步代码都执行完毕后, JS会不断检测 事件循环中的异步代码是否满足条件

  • 一旦满足条件就执行满足条件的异步代码

3.宏任务和微任务

在JS的异步代码中又区分"宏任务(MacroTask)"和"微任务(MicroTask)"

宏任务: 宏/大的意思, 可以理解为比较费时比较慢的任务

微任务: 微/小的意思, 可以理解为相对没那么费时没那么慢的任务

4.常见的宏任务和微任务

宏任务: setTimeout, setInterval

微任务: Promise, MutationObserver,process.nextTick(node独有)

注意点

所有的宏任务和微任务都会放到自己的执行队列中, 也就是有一个宏任务队列和一个微任务队列

所有放到队列中的任务都采用"先进先出原则"(先放进去的任务会先执行,后放进去的任务会后执行), 也就是多个任务同时满足条件, 那么会先执行先放进去的

5.演示

上面的基本宏任务和微任务都了解过,process.nextTick这个留着下篇文章再讲

本节先来看看这个MutationObserver

MutationObserver是专门用于监听节点的变化

下面简单来写个例子

<div></div>
<button class="add">添加节点</button>
<button class="del">删除节点</button>
<script>
  // 1.获取元素
  let oDiv = document.querySelector('div');
  let oAddBtn = document.querySelector('.add');
  let oDelBtn = document.querySelector('.del');
  // 2.监听点击事件
  oAddBtn.onclick = () => {
    let op = document.createElement("p");
    op.innerText = "我是段落";
    oDiv.appendChild(op);
  }
  oDelBtn.onclick = () => {
    let op = document.querySelector("p");
    oDiv.removeChild(op);
  }
  // 3.创建MutationObserver任务
  let mb = new MutationObserver(() => {
    console.log('节点发生了变化');
  })
  // 4.监听对象(下面代码含义: 监听oDiv元素子节点的变化)
  mb.observe(oDiv, {
    "childList": true
  })
  // 5.如何验证MutationObserver是微任务(异步)
  // 下面再加点同步代码
  // 如果同步代码先执行,是不是就表示确实是异步代码
  console.log("同步代码Start");
  console.log("同步代码End");
  </script>

6.完整执行顺序

  • 从上至下执行所有同步代码

  • 在执行过程中遇到宏任务就放到宏任务队列中,遇到微任务就放到微任务队列中

  • 当所有同步代码执行完毕之后, 就执行微任务队列中满足需求所有回调

  • 当微任务队列所有满足需求回调执行完毕之后, 就执行宏任务队列中满足需求所有回调

注意点

每执行完一个宏任务都会立刻检查微任务队列有没有被清空, 如果有就满足的微任务就立即执行微任务

那下面来看两个例子应该就懂了

例子一

  setTimeout(function () {
      console.log("setTimeout1");
  }, 0);
  Promise.resolve().then(function () {
      console.log("Promise1");
  });
  console.log("同步代码Start");
  Promise.resolve().then(function () {
      console.log("Promise2");
  });
  setTimeout(function () {
      console.log("setTimeout2");
  }, 0);
  console.log("同步代码End");

我们来简单分析,上面是不是说过,有同步代码先执行同步代码,异步代码遇到自己对应的任务队列就按先后顺序排放

所以先输出的肯定是同步代码start和同步代码end

之后是不是先执行微任务的代码,按照先进先出的原则,是不是p1先进来,p2后进来,p1和p2是不是都满足条件 可以立即执行

所以静跟着输出的结果是Promise1和Promise2

微任务执行完了是不是就去执行宏任务

宏任务中是不是s1先进来,s2后进来,是不是延迟0秒就执行,是不是都满足条件

所以最后输出的就是setTimeout1和setTimeout2

怎么验证我的说法?

看看浏览器打印出来的结果

是不是跟我说的一模一样

看完了例子一,来看例子二

例子二

  setTimeout(function () {
    console.log("setTimeout1");
    Promise.resolve().then(function () {
      console.log("Promise1");
    });
    Promise.resolve().then(function () {
      console.log("Promise2");
    });
  }, 0);
  setTimeout(function () {
    console.log("setTimeout2");
    Promise.resolve().then(function () {
      console.log("Promise3");
    });
    Promise.resolve().then(function () {
      console.log("Promise4");
    });
  }, 0);

我还是简单来说一下

从上至下进行执行,首先先遇到一个setTimeout,这个是不是一个宏任务,然后就这个放入宏任务的队列

之后遇到一个setTimeout,这个是不是也是一个宏任务,然后又把这个放入宏任务的队列里

(有人肯定说,诶你Promise为什么不放入宏任务,因为promise是setTimeout里面的东西,我们执行代码是不是先执行外面的,再执行里面的,又因为setTimeout是不是一个异步代码,就不会立即执行,所以promise还没有执行就不存在放入微任务里面,这应该属于基本常识,我就不做过多解释了)

这样我们的代码是不是就执行完毕了,执行完毕我们来看一下

假如前面的setTimeout叫做s1,后面的叫做s2,,因为内存中没有微任务,是不是就直接执行宏任务了

是不是s1先进入,s2后进入,采取先进先出的原则,就先执行s1,先执行s1,遇到一个打印setTimeout1,

这是不是一个同步代码,就立即执行了,然后遇到一个Promise1,这是不是一个微任务,就把p1放入微任务队列里然后又执行遇到一个promise2,也是一个微任务,然后放入微任务里

这样s1的代码是不是就执行完毕了,输出结果是不是setTimeout1

上面是不是说过每执行完一个宏任务,就会去看微任务里有没有可以执行的微任务,我们执行完s1,是不是微任务里面有两个promise,是不是都满足条件可以执行

所以紧接着我们就会把s2先放一下,执行微任务,是不是p1先进入,p2后进入,采用先进先出的原则,先执行p1 ,再执行p2,紧接着就输出了promise1和promise2

这样微任务是不是都执行完了,微任务执行完了就会去执行宏任务,宏任务还有一个s2,s2是不是延迟0s就表示立即执行,是不是满足条件可以执行

执行遇到一个同步代码就直接输出setTimeout2,之后遇到一个p3微任务,p4微任务,是不是和前面一样,放入微任务队列,s2就执行完毕了

s2执行完毕会看微任务有没有可以执行的任务,p3和p4是不是都满足,所以按着先后顺序,是不是就打印promise3和promise4

怎么验证我的说法

看看浏览器打印的结果

是不是没有任何的问题了

听懂了我还留了一个小练习

7.小练习

  setTimeout(function () {
      console.log("setTimeout1");
      Promise.resolve().then(function () {
          console.log("Promise2");
      });
      Promise.resolve().then(function () {
          console.log("Promise3");
      });
  }, 0);
  Promise.resolve().then(function () {
      console.log("Promise1");
      setTimeout(function () {
          console.log("setTimeout2");
      });
      setTimeout(function () {
          console.log("setTimeout3");
      });
  });

请把输出结果答在评论区(qvq)