浏览器事件循环(事件轮询)

浏览器事件循环(事件轮询)

1.浏览器事件循环流程

浏览器事件循环(Browser Event Loop)是浏览器用于处理用户输入、网络请求、渲染和其他异步事件的机制。这个循环确保了 JavaScript 代码的执行是非阻塞的,允许浏览器同时处理多个任务,从而提高用户体验。以下是浏览器事件循环的详细说明:

  1. 调用栈: 当一个 JavaScript 脚本开始执行时,它会被放入调用栈。调用栈是一个数据结构,用于跟踪执行上下文(函数调用)的堆栈。

  2. 同步任务: 在调用栈中的代码是同步任务,它们会按照执行的顺序逐一执行。如果有函数调用,它们会被压入调用栈,直到执行完成。

  3. 异步任务触发: 当浏览器遇到异步任务,例如定时器、事件监听器、网络请求等,它会将这些任务放入任务队列中。

  4. 任务队列: 任务队列是一个先进先出(FIFO)的数据结构,用于存储异步任务。有多个任务队列,其中包括宏任务队列和微任务队列)。

    • 宏任务队列: 包括 DOM 操作、用户交互事件、定时器等。宏任务完成后,会将一个新的同步任务放入调用栈。

    • 微任务队列: 包括 Promise 回调、MutationObserver 回调等。微任务会在当前宏任务执行完成后、下一个宏任务执行前执行。

  5. 事件循环(Event Loop): 当调用栈为空时,事件循环开始工作。它会检查宏任务队列,如果有任务,将任务推入调用栈执行。执行完毕后,再检查微任务队列,如果有微任务,将其依次推入调用栈执行。这个过程会一直重复,形成一个循环。

    • 宏任务执行: 从宏任务队列中选择一个任务,执行完毕后,再选择下一个宏任务。

    • 微任务执行: 在宏任务执行完毕后,依次执行微任务队列中的所有任务。

这个事件循环的机制确保了 JavaScript 的异步执行,同时避免了阻塞主线程。这对于处理用户交互、网络请求等异步任务是非常重要的,以确保应用程序的响应性和性能。

2.同步任务、异步任务、宏任务和微任务概念

同步任务:

同步任务是按照它们被调用的顺序依次执行的任务,一个接一个地在调用栈中运行。在执行同步任务时,JavaScript 引擎会一直等待任务执行完毕,然后才继续执行下一个任务。

javascript 复制代码
console.log('Task 1');
console.log('Task 2');
console.log('Task 3');
// ...

异步任务:

异步任务是不会阻塞后续代码执行的任务。它们将在将来的某个时间点执行,可以是由浏览器环境触发的事件,也可以是由开发者手动触发的异步操作。

javascript 复制代码
// 异步任务示例:定时器
setTimeout(function() {
  console.log('1000ms');
}, 1000);

// 异步任务示例:事件监听器
document.addEventListener('click', function() {
  console.log('点击');
});

宏任务:

宏任务是由浏览器环境提供的任务,包括 I/O 操作、渲染、事件处理等。在事件循环的每一轮中,只会执行一个宏任务。常见的宏任务包括 setTimeout、setInterval、DOM 操作、AJAX 请求等。

javascript 复制代码
// 宏任务示例:setTimeout
setTimeout(function() {
  console.log('2000ms');
}, 2000);

// 宏任务示例:AJAX 请求
fetch('https://api.abc.com/data')
  .then(response => response.json())
  .then(data => console.log('AJAX'));

微任务:

微任务是在当前宏任务执行完毕后、下一个宏任务执行前触发的任务。它们有着更高的优先级,会在宏任务中的异步操作之前执行。常见的微任务包括 Promise 的回调、MutationObserver 等。

javascript 复制代码
// 微任务示例:Promise
Promise.resolve().then(function() {
  console.log('微任务1');
});

// 微任务示例:MutationObserver
const observer = new MutationObserver(function() {
  console.log('微任务2');
});

observer.observe(document.body, { attributes: true });

document.body.setAttribute('class', 'some-class');

在事件循环中,首先执行当前调用栈中的同步任务,然后检查并执行宏任务队列中的一个宏任务,接着执行微任务队列中的所有微任务。这个过程会一直重复,确保了 JavaScript 引擎的异步执行和非阻塞特性。

3.宏任务和微任务区别

宏任务和微任务都是异步任务的一种,但它们之间存在一些区别。

异步任务的分类:

  1. 宏任务: 包括整体的代码、setTimeout、setInterval、AJAX 请求、DOM 操作等。宏任务会在当前调用栈执行完毕后,从宏任务队列中取出一个任务执行。

  2. 微任务: 包括 Promise 的回调、MutationObserver、process.nextTick 等。微任务会在当前宏任务执行完毕后、下一个宏任务执行前触发,且微任务会在宏任务中的异步操作之前执行。

异步任务执行顺序:

  1. 执行同步任务,按照代码顺序逐一执行。
  2. 执行当前宏任务,从宏任务队列中取出一个任务执行。
  3. 执行微任务队列中的所有微任务。
  4. 重复步骤 2 和 3,直到宏任务队列为空。
javascript 复制代码
console.log('同步 1');

// 宏任务
setTimeout(function() {
  console.log('宏任务 1');

  // 微任务
  Promise.resolve().then(function() {
    console.log('微任务 1');
  });
}, 0);

// 宏任务
setTimeout(function() {
  console.log('宏任务 2');

  // 微任务
  Promise.resolve().then(function() {
    console.log('微任务 2');
  });
}, 0);

console.log('同步 2');
//同步 1
//同步 2
//宏任务 1
//微任务 1
//宏任务 2
//微任务 2

在上面的示例中,同步任务(同步1、同步2)首先执行,然后是两个宏任务(宏任务1、宏任务2)。在每个宏任务执行后,会依次执行微任务(微任务1、微任务2)。这种执行顺序确保了微任务比宏任务更具优先级,微任务会在下一个宏任务之前执行。

  1. 首先,执行同步代码,输出 '同步 1' 和 '同步 2'。
  2. 然后,两个 setTimeout 中的回调函数被分别添加到宏任务队列。
  3. 接着,执行微任务队列中的任务,即两个 Promise 的回调函数。输出 '微任务 1' 和 '微任务 2'。
  4. 再次回到宏任务队列,执行第一个 setTimeout 的回调函数,输出 '宏任务 1'。在这个宏任务中,又产生了一个微任务,即 Promise 的回调函数,输出 '微任务 1'。
  5. 继续执行宏任务队列,执行第二个 setTimeout 的回调函数,输出 '宏任务 2'。同样,在这个宏任务中,产生了一个微任务,即 Promise 的回调函数,输出 '微任务 2'。

微任务总是在当前宏任务执行完毕后、下一个宏任务执行前执行。微任务 1 和 微任务 2 在它们所属的宏任务(setTimeout 的回调函数)执行完毕后才得以执行。

4.Vue中涉及事件循环

在Vue.js中,事件循环主要涉及到Vue实例的生命周期、响应式数据的更新、以及Vue异步操作的处理。

  1. Vue生命周期钩子:

    • Vue实例在创建、挂载、更新、销毁等阶段都有对应的生命周期钩子函数。
    • 这些生命周期钩子函数在特定的时机被触发,它们在事件循环中的执行顺序受到影响,从而影响Vue实例的行为。
    • 例如,在created生命周期钩子中,Vue实例已经创建,但尚未挂载到DOM中。在这个阶段,可以进行一些异步操作,这些异步操作会在事件循环的下一个周期中执行。
  2. 数据更新响应:

    • Vue的响应式系统通过数据的变化来触发视图的更新。
    • 当数据发生变化时,Vue会通过事件循环的微任务队列将更新操作推送到队列中,然后在当前任务执行完成后立即执行微任务队列中的更新操作。
    • 这保证了数据更新的响应性,同时避免了在同一个任务中频繁地进行DOM更新,提高了性能。
  3. Vue.nextTick方法:

    • Vue.nextTick是Vue提供的一个工具方法,用于在DOM更新后执行回调函数。
    • 在某些场景下,比如修改了数据但想要立即获取更新后的DOM状态,可以使用Vue.nextTick来确保在下一次事件循环中执行回调。
    • 这是因为DOM更新是异步的,Vue.nextTick会将回调函数推送到微任务队列中,确保在DOM更新后执行。
  4. 异步组件加载:

    • Vue支持异步组件加载,通过import语法实现。当使用异步组件时,组件的加载是异步的。
    • 异步组件的加载过程涉及到事件循环,确保在组件加载完成后才会进行渲染。
    • 这有助于提高应用的性能,避免一开始就加载所有组件,而是按需加载。
  5. Vue异步操作:

    • 在Vue中,一些异步操作,比如$nextTick$set等,都涉及到事件循环的概念。
    • 通过这些异步操作,Vue能够在下一个事件循环周期中执行一些需要等待的任务,以确保在适当的时机进行DOM更新或其他操作。

5.$nextTick

在Vue中,一个经典的例子是使用this.$nextTick来确保在DOM更新完成后执行一些操作。这在处理DOM更新的时候非常有用,特别是当需要获取更新后的DOM状态时。

假设有一个按钮,点击按钮后触发显示一个Element UI的Modal对话框,并且想在Modal对话框显示后获取它的某些属性,例如宽度。

html 复制代码
<template>
  <div>
    <el-button @click="showModal">显示Modal</el-button>
    <el-dialog :visible.sync="dialogVisible" title="我是一个Dialog">
      <!-- Modal 内容 -->
      <!-- ... -->
    </el-dialog>
  </div>
</template>

<script>
export default {
  data() {
    return {
      dialogVisible: false,
      modalWidth: null
    };
  },
  methods: {
    showModal() {
      this.dialogVisible = true;

      // 此时 Modal 还未渲染到 DOM 上
      console.log('Modal 尚未渲染到 DOM 上,此时宽度为:', this.modalWidth);

      // 使用 $nextTick 来确保在下一次事件循环中执行回调
      this.$nextTick(() => {
        // 此时 Modal 已经渲染到 DOM 上
        this.modalWidth = this.$refs.dialog.$el.clientWidth;
        console.log('Modal 已渲染到 DOM 上,宽度为:', this.modalWidth);
      });
    }
  }
};
</script>

当按钮被点击时,showModal方法会设置dialogVisibletrue,显示Modal。然后使用this.$nextTick来确保在下一次事件循环中执行回调函数,这个回调函数用于获取Modal对话框的宽度。通过这种方式,能够确保在Modal渲染到DOM上后再去获取其属性,避免了在Modal还未渲染完成时就尝试获取其属性的问题。

相关推荐
开心工作室_kaic6 分钟前
基于微信小程序的校园失物招领系统的设计与实现(论文+源码)_kaic
c语言·javascript·数据库·vue.js·c#·旅游·actionscript
Small-K7 分钟前
前端框架中@路径别名原理和配置
前端·webpack·typescript·前端框架·vite
bin915311 分钟前
【EXCEL数据处理】000009 案列 EXCEL单元格数字格式。文本型数字格式和常规型数字格式的区别
大数据·前端·数据库·信息可视化·数据分析·excel·数据可视化
山语山29 分钟前
C语言——文件读写操作
java·c语言·前端·microsoft·visual studio
太阳火神的美丽人生1 小时前
Vant WeApp 开启 NPM 遇到的问题总结
前端·npm·node.js
哈哈哈哈cwl1 小时前
一篇打通浏览器储存
前端·面试·浏览器
小迷糊糊NWCX1 小时前
【JAVA面试】关于接口
面试·职场和发展
customer082 小时前
【开源免费】基于SpringBoot+Vue.JS洗衣店订单管理系统(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
杨荧2 小时前
【JAVA开源】基于Vue和SpringBoot的周边产品销售网站
java·开发语言·vue.js·spring boot·spring cloud·开源
凌云行者2 小时前
使用rust写一个Web服务器——async-std版本
服务器·前端·rust