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

这篇具有很好参考价值的文章主要介绍了浏览器事件循环(事件轮询)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

1.浏览器事件循环流程

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

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

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

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

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

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

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

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

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

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

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

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

同步任务:

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

console.log('Task 1');
console.log('Task 2');
console.log('Task 3');
// ...
异步任务:

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

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

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

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

// 宏任务示例: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 等。

// 微任务示例: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,直到宏任务队列为空。
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 的回调函数)执行完毕后才得以执行。

ps:

new Promise((resolve, reject) => {
  console.log("fn12");
  resolve();
  new Promise((resolve, reject) => {
  console.log("fn13");
  resolve();
  new Promise((resolve, reject) => {
  console.log("fn14");
  resolve();
  }).then(function () {
  console.log("fn15");
  });
  }).then(function () {
  console.log("fn16");
  });
  }).then(function () {
  console.log("fn17");
  });
//fn12
//fn13
//fn14
//fn15
//fn16
//fn17
  1. 首先,console.log("fn12"); 执行,立即输出 fn12
  2. 然后,第一个 Promise 解决 (resolve())。但由于其 .then() 部分(包含 console.log("fn17");)被放置在最外层,所以它将在整个 Promise 链被解析之后才执行。
  3. 接下来,代码进入第二个 Promise,并执行 console.log("fn13");,立即输出 fn13
  4. 第二个 Promise 被解决。但是,其 .then() 部分(包含 console.log("fn16");)同样需要等待更内层的 Promise 解决才执行。
  5. 然后,代码进入第三个 Promise,并执行 console.log("fn14");,立即输出 fn14
  6. 第三个 Promise 被解决,其 .then() 部分(包含 console.log("fn15");)被放置在微任务队列中,并准备执行。

同步代码执行完毕,事件循环开始处理微任务队列:

  1. 首先执行第三个 Promise 的 .then() 回调,输出 fn15
  2. 接下来是第二个 Promise 的 .then() 回调,输出 fn16
  3. 最后执行第一个 Promise 的 .then() 回调,输出 fn17

这个顺序的关键是理解 Promise 的 .then() 部分是如何被放置在微任务队列中的,以及它们是如何根据嵌套结构被逐一解决的。每个 .then() 只有在其相应的 Promise 被解决后才会被放入队列,且内层的 Promise 必须在外层的 Promise 之前解决。因此,虽然 fn17 是第一个 .then() 的回调,但它被放置在微任务队列中的顺序实际上是在 fn15 和 fn16 之后。

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对话框显示后获取它的某些属性,例如宽度。

<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还未渲染完成时就尝试获取其属性的问题。文章来源地址https://www.toymoban.com/news/detail-803318.html

到了这里,关于浏览器事件循环(事件轮询)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 在JavaScript中,什么是浏览器事件循环(browser event loop)?

    前端入门之旅:探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅!这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发者,这里都将为你提供一

    2024年02月07日
    浏览(44)
  • 前端---需要了解浏览器相关知识--浏览器请求服务器资源---缓存

    掘金1:浏览器缓存 掘金2 :浏览器缓存 跟别人怎么讲,从大的说:缓存的原理是什么? 再说什么是浏览器缓存? 浏览器缓存 请求(静态资源 | 动态资源) 一、缓存是什么? 二、为什么? 浏览器是如何判断是否使用缓存的??第一次请求网页 第二次请求相同网页: 三、怎

    2024年02月12日
    浏览(40)
  • 计算机网络、浏览器相关高频面试题

    没有使用CDN的情况下,用户从浏览器输入地址,依次经过浏览器缓存、操作系统缓存(如本地host文件)、域名解析服务器、根域名解析服务器、顶级域名服务器直到找到对应的ip地址返回给用户,用户向该地址发起请求; 使用了CDN的情况下,用户在浏览器中输入要访问的域名

    2024年01月25日
    浏览(39)
  • 前端高级面试题-浏览器

    1 事件机制 事件触发三阶段 document 往事件触发处传播,遇到注册的捕获事件会触发 传播到事件触发处时触发注册的事件 从事件触发处往 document 传播,遇到注册的冒泡事件会触发 注册事件 通常我们使⽤ addEventListener 注册事件,该函数的第三个参数可以是布尔值,也可以是对

    2024年02月15日
    浏览(37)
  • 谈一谈浏览器与Node.js中的JavaScript事件循环,宏任务与微任务机制

    JavaScript是一个单线程非阻塞的脚本语言。这代表代码是执行在一个主线程上面的。但是JavaScript中有很多耗时的异步操作,例如AJAX,setTimeout等等;也有很多事件,例如用户触发的点击事件,鼠标事件等等。这些异步操作并不会阻塞我们代码的执行。例如: 可以看到,上述代

    2024年02月12日
    浏览(37)
  • web大前端面试——浏览器、网络和安全

    (1).DNS解析 当我们在浏览器中输入一个域名的时候,例如www.mi.com,这个域名只是与IP地址的一个映射,这时DNS解析就要充当一个翻译的角色,把域名解析成真实的IP地址。所以DNS解析的过程实际上就是将域名还原成真实IP地址的过程。 DNS的解析又分为以下几个步骤: 1.浏览器会

    2024年02月03日
    浏览(36)
  • 前端浏览器缓存知识梳理,前端工程师面试题目和答案

    所谓浏览器缓存其实就是指在本地使用的计算机中开辟一个内存区,同时也开辟一个硬盘区作为数据传输的缓冲区,然后用这个缓冲区来暂时保存用户以前访问过的信息。 浏览器缓存过程:  强缓存,协商缓存。 浏览器缓存位置一般分为四类:  Service Worker–Memory Cache–Di

    2024年04月15日
    浏览(39)
  • 前端面试:【浏览器与渲染引擎】工作原理与渲染流程

    嗨,亲爱的读者!你是否曾经好奇过当你在浏览器中输入URL并按下回车时,网页是如何显示在你的屏幕上的?这背后涉及了复杂的浏览器工作原理和渲染流程。本文将带你深入了解浏览器如何工作以及网页如何被渲染出来。 1. 浏览器的工作原理: 当你输入URL并按下回车时,

    2024年02月11日
    浏览(42)
  • 【学姐面试宝典】—— 前端基础篇Ⅱ(HTTP/HTML/浏览器)

    前言 博主主页👉🏻蜡笔雏田学代码 专栏链接👉🏻【前端面试专栏】 今天继续学习前端面试题相关的知识! 感兴趣的小伙伴一起来看看吧~🤞 作用是 Doctype 声明于文档最前面,告诉浏览器以何种方式来渲染页面。 这里有两种模式, 严格模式 和 混杂模式 。 严格模式的排

    2024年01月25日
    浏览(38)
  • 【学姐面试宝典】—— 前端基础篇Ⅰ(HTTP/HTML/浏览器)

    前言 博主主页👉🏻蜡笔雏田学代码 专栏链接👉🏻【前端面试专栏】 今天开始学习前端面试题相关的知识! 感兴趣的小伙伴一起来看看吧~🤞 http: 超文本传输协议,是互联网上应用最为广泛的一种网络协议,是一个客户端和服 务器端请求和应答的标准(TCP),用于从 WW

    2024年02月19日
    浏览(27)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包