事件循环

130 阅读2分钟

JS 处理多任务

任何一个程序在执行期间都可能会开启多个任务,比如:

  1. 程序启动时开始进行一些操作
  2. 开启一个计时器,每隔一段时间去做一些事
  3. 监听按钮是否被点击,当按钮被点击后,去做一些事

由于 JS 的执行线程只有一个,因此,它通过异步的方式来解决这些任务

同步代码 和 异步代码

同步代码:程序启动后,在 JS 执行线程上立即执行的任务代码

异步代码:收到宿主环境(浏览器)的其它线程通知,即将在 JS 执行线程上执行的代码,例如计时器回调函数中的代码,事件中的代码。JS 中的异步代码往往放到一个函数中,该函数成为异步函数,或者描述为,该函数是异步的

执行栈

为了保证 JS 代码有序的执行,JS 执行引擎使用执行栈来组织 JS 代码

每当调用一个函数时,都会在执行栈中创建一个执行上下文,上下文中提供了函数执行需要的环境,创建了上下文之后,再执行函数

事件循环

事件循环是 JS 处理异步函数的具体方法

具体的做法是:

  1. 执行 执行栈 中的代码
  2. 遇到一些特殊代码交给浏览器的其他线程处理
  3. 将执行栈中的代码全部执行完毕
  4. 从事件队列中取出第一个任务放入执行栈,然后重复第 1 步

事件队列在不同的宿主环境中有所差异,大部分宿主环境会将事件队列进行细分。在浏览器中,事件队列分为两种:

  • 宏任务(队列):macroTask,计时器结束的回调、事件回调、http 回调等等绝大部分异步函数进入宏队列
  • 微任务(队列):microTask,Promise.then, [MutationObserver]

当执行栈清空时,JS 引擎首先会将微任务中的所有任务依次执行结束,如果没有微任务,则执行宏任务。

<ul class="list"></ul>
<script>
  var oUl = document.getElementsByClassName('list')[0];

  setTimeout(function () {
    console.log('a');
  }, 0);

  var observer = new MutationObserver(function () {
    console.log('b');
  });

  observer.observe(oUl, {
    childList: true,
  });

  oUl.innerHTML = '<li>我是新增的li标签</li>';
  console.log('c');
</script>