一文带你彻底弄懂js事件循环(Event Loop)

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

JavaScript事件循环是JavaScript运行时环境中处理异步操作的机制。它允许JavaScript在执行同步代码的同时处理异步任务,以避免阻塞线程并提供更好的用户体验。

本文将在浏览器异步执行原理基础上带你彻底弄懂js的事件循环机制。

浏览器JS异步执行原理

js是单线程的,也就是说同一时刻只能做一件事情。
作为一个浏览器脚本语言,这与javascript的用途有关。

JavaScript主要用于与用户进行交互和操作文档对象模型(DOM)。在网页中,用户的操作和页面的渲染是非常重要的,如果JavaScript是多线程的,可能会导致多个线程同时修改DOM,造成不可预测的结果。

因此,为了保证页面的安全性和稳定性,JavaScript被设计为单线程的。它使用事件循环机制来处理异步任务,确保任务的按序执行,避免了多线程带来的竞态条件和死锁等问题。

虽然JavaScript是单线程的,但是通过异步编程模型,可以实现非阻塞的操作。例如,通过使用回调函数、Promise、async/await等方式,可以在执行异步任务时,不会阻塞后续的代码执行,从而提高了程序的响应性能。

需要注意的是,虽然JavaScript本身是单线程的,但是浏览器是多线程的。浏览器中除了JavaScript引擎线程外,还有渲染线程、网络线程、定时器线程等。这些线程可以并行执行,提高了浏览器的性能和用户体验。但是JavaScript代码本身在执行时仍然是单线程的。
一文带你彻底弄懂js事件循环(Event Loop),javascript,开发语言

执行栈与任务队列

1. 执行栈(Call Stack):
执行栈是一种数据结构,用于管理函数的执行上下文(execution context)。当函数被调用时,会创建一个对应的执行上下文,并将其推入执行栈的顶部。执行栈遵循先进后出(LIFO)的原则,即最后进入的执行上下文最先执行,执行完毕后会从栈顶弹出。

当JavaScript引擎执行代码时,会将函数调用和表达式求值的过程以栈的形式进行管理。每当遇到函数调用时,会将该函数的执行上下文推入执行栈,然后执行函数体中的代码。如果函数内部又调用了其他函数,会将新的执行上下文推入执行栈,形成嵌套的执行上下文。

当函数执行完毕或遇到return语句时,会将该执行上下文从执行栈中弹出,继续执行之前的上下文。当执行栈为空时,表示所有的函数都已执行完毕,程序结束。

2. 任务队列(Task Queue):
任务队列是一种用于存储待执行的任务的队列。在JavaScript中,任务队列主要用于存储异步任务的回调函数。

当遇到异步任务(如定时器、网络请求、事件监听等)时,会将相应的回调函数放入任务队列中,而不会立即执行。当执行栈为空时,JavaScript引擎会从任务队列中取出一个任务,将其对应的回调函数推入执行栈,开始执行。

任务队列采用先进先出(FIFO)的原则,即先进入队列的任务会先被执行。这保证了异步任务按照其被触发的顺序执行,避免了回调函数的竞争和混乱。

通过执行栈和任务队列的协作,JavaScript实现了异步编程模型,使得程序能够在等待异步任务完成的同时继续执行其他任务,提高了程序的并发性和响应性能。

我们看下面一个列子更深入的理解一下执行栈和任务队列这个概念:

   console.log('1')
   setTimeout(() => {
     console.log('2')
   },0)
   console.log('3')
   //1 3 2

一文带你彻底弄懂js事件循环(Event Loop),javascript,开发语言
由上图可以清晰的看出,同步代码直接放入执行栈立即执行,而异步代码则被放入了宿主环境中,而宿主环境中的代码会等待正确的时机,时机一到宿主环境会把里面的回调函数推送给任务队列执行栈执行完之后会看任务队列里面有没有异步任务,有则把里面的代码推送到执行栈中执行。

宏任务和微任务

任务队列中的任务分为宏任务(macro-task)和微任务(micro-task)两种类型。宏任务包括定时器任务、事件任务等,而微任务主要包括Promise的回调函数、MutationObserver的回调函数等。微任务的执行优先级高于宏任务,即在执行栈为空时,会先执行所有的微任务,然后再执行宏任务。

1. 宏任务(macro-task)(宿主环境-浏览器、node):
宏任务是一类较为重量级的任务,包括但不限于以下几种:

  • 定时器任务(setTimeout、setInterval等)
  • I/O任务(文件读写、网络请求等)
  • UI渲染任务(页面重绘、动画渲染等)
  • 事件任务(鼠标点击、键盘事件等)

宏任务会被推入任务队列中,在执行栈为空时,JavaScript引擎会从任务队列中取出一个宏任务,将其对应的回调函数推入执行栈,开始执行。宏任务之间的执行顺序是按照它们被添加到任务队列的顺序来执行的。

2. 微任务(micro-task)(js引擎):
微任务是一类较为轻量级的任务,主要包括以下几种:

  • Promise的回调函数(then、catch、finally)
  • MutationObserver的回调函数
  • process.nextTick(Node.js环境)

微任务会在当前宏任务执行完毕后立即执行,而不需要等待其他宏任务。当执行栈为空时,JavaScript引擎会先执行所有的微任务,然后再执行下一个宏任务。微任务之间的执行顺序是按照它们被添加到任务队列的顺序来执行的。
接着上面的列子再来分析一下:

    console.log('1')
    setTimeout(() => {
      console.log('2')
    },0)
    new Promise(function (resolve) {
	    console.log('promise1')
	    resolve()
        console.log('promise2')
    }).then(function () {
        console.log('promise3')
    })
    console.log('3')
    //1 promise1 promise2 3 promise3 2

一文带你彻底弄懂js事件循环(Event Loop),javascript,开发语言
上图可清晰的看出,首先执行执行栈中的任务,依次打印1-promise1-promise2-3,然后把微任务中的任务放到执行栈中,打印promise3,微任务队列执行完之后,再把宏任务中的队列放到执行栈中执行,打印2

总结

javaScript事件循环是一种用于管理和调度异步代码执行的机制。它的核心思想是通过执行栈、任务队列和事件触发来实现异步编程。

事件循环的执行过程可以总结如下:

1. 执行全局同步代码:
首先,JavaScript引擎会执行全局上下文中的同步代码,将函数调用和表达式求值的过程以栈的形式进行管理,即执行栈(Call Stack)。

2. 处理微任务:
在执行全局同步代码的过程中,如果遇到微任务(Promise的回调函数、MutationObserver的回调函数等),会将其推入微任务队列。

3. 处理宏任务:
当执行栈为空时,JavaScript引擎会从任务队列中取出一个宏任务,将其对应的回调函数推入执行栈,开始执行。宏任务包括定时器任务(setTimeout、setInterval等)、I/O任务(文件读写、网络请求等)、UI渲染任务(页面重绘、动画渲染等)和事件任务(鼠标点击、键盘事件等)。

4. 处理微任务:
在执行完一个宏任务后,会检查微任务队列是否为空,如果不为空,则依次执行所有的微任务。微任务的执行优先级高于宏任务,即在同一个宏任务中,会先执行所有的微任务,然后再执行下一个宏任务。

5. 重复执行步骤3和步骤4:
事件循环会不断重复执行步骤3和步骤4,直到执行栈和任务队列都为空。

需要注意的是,事件循环是单线程的,即一次只能执行一个任务。当一个任务正在执行时,其他任务需要等待。这也是为什么长时间运行的任务会阻塞UI渲染和其他任务的原因。

合理地使用异步编程和事件循环机制可以提高程序的性能和响应性能,避免了阻塞和卡顿。同时,需要注意避免过多的嵌套回调和过度依赖异步操作,以免造成代码可读性和维护性的问题。文章来源地址https://www.toymoban.com/news/detail-719958.html

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

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

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

相关文章

  • 一文彻底弄懂ConcurrentHashMap

    前面分析 HashMap 的文章,提到过 HashMap 是线程不安全的,其主要原因还是在链表扩容。 JDK1.7 的 HashMap 的扩容操作用到两个方法: resize() 和 transfer() ,主要是重新定位每个桶的下标,并采用 头插法 将元素迁移到新的数组中。假设有多个线程都对 HashMap 进行扩容,有可能扩容

    2024年02月05日
    浏览(32)
  • 一篇文章带你彻底弄懂Java的==符号

    本篇文章6735字,大概阅读时间20分钟。本文中使用到的JDK版本为1.8.0_301 目录 ==符号的定义 基本类型中==符号的判断 String类型中==符号的判断         在Java中==符号的作用分为两类:         1:==符号在八种基本类型的作用是比较对应基本类型的 数值是否相等         2:

    2024年02月08日
    浏览(41)
  • [javascript核心-04]彻底弄懂Promise异步编程

    本文github地址:JavaScript_Interview_Everything 大前端知识体系与面试宝典,从前端到后端,全栈工程师,成为六边形战士 1.1. 快速上手 01快手上手.js 02.若传入的是另一个promise对象,则状态由传入的promise对象决定 03.若传入了一个实现了 then 方法的对象,则执行该then方法且由此方法

    2024年02月08日
    浏览(29)
  • 【Java】一文彻底弄懂访问修饰符(public/protected/默认/private)--建议收藏

    博主简介: 努力学习的预备程序媛一枚~ 博主主页: @是瑶瑶子啦 所属专栏: Java岛冒险记【从小白到大佬之路】  OOP(Object Oriented Programing),即面向对象编程,最重要的功能/特点之一就是 封装 ,这点在该专栏开篇博客【Java基础篇】Java重要特性,JDK,JRE,JVM区别和联系,环境变量

    2024年02月22日
    浏览(36)
  • Mybatis中的缓存机制(一文带你弄懂)

    缓存的作⽤:通过减少IO的⽅式,来提⾼程序的执⾏效率。 缓存就是指存在内存中的临时数据,使用缓存能够减少和数据库交互的次数,提高效率。将相同查询条件的sql语句执行一遍后得到的结果存在内存或者某种缓存介质中,当下次遇到一模一样的查询sql时候不在执行sql与

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

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

    2024年02月12日
    浏览(38)
  • C#学习笔记--foreach循环是怎么遍历容器的?实战带你弄懂foreach原理

    上篇文章我们了解了 IEnumerable 和 IEnumerator C#学习笔记–由浅至深理解IEnumerable和IEnumerator 这篇文章我们来研究 foreach 循环 foreach 循环实际上是通过调用集合的 GetEnumerator 方法来获取一个枚举器,然后在每次迭代中调用枚举器的 MoveNext 方法来遍历集合的。当枚举器的 MoveNext 方

    2024年02月03日
    浏览(26)
  • 【MySQL】一文带你彻底了解事务机制

    我们设想一个场景,这个场景中我们需要插入多条相关联的数据到数据库,不幸的是,这个过程可能会遇到下面这些问题: 数据库中途突然因为某些原因挂掉了。 客户端突然因为网络原因连接不上数据库了。 并发访问数据库时,多个线程同时写入数据库,覆盖了彼此的更改

    2024年02月09日
    浏览(28)
  • Java数据类型,一文带你彻底拿捏~

    ——Java中运算符是一种特殊的符号,用来进行数据的运算、赋值和比较等 思维导图 1.什么是算术运算符         ——算术运算符是用于数据类型值之间,使用2个或以上的数据进行运算 2.算术运算符概括 算术运算符 解释 示例 +,- 正号,负号 +10,-10 +,- 加,减 10 + 10,1

    2024年02月13日
    浏览(29)
  • JS 之 事件Event对象详解(属性、方法、自定义事件)

    一、Event对象 1、简介 ​ 事件 event 对象是指在浏览器中触发事件时,浏览器会自动创建一个 event 对象,其中存储了本次事件相关的信息,包括事件类型、事件目标、触发元素等等。浏览器创建完 event 对象之后,会自动将该对象作为参数传递给绑定的事件处理函数,我们可以

    2024年02月09日
    浏览(56)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包