当前位置: 首页 > Web前端 > HTML

什么是事件循环?!为什么总是和异步任务一起说呢?

时间:2023-03-29 11:11:53 HTML

众所周知,JavaScript是单线程的,但是现在经常需要JavaScript来执行一些异步任务。这时候就会用到setTimeout、asyncawait、promise等API。也许你要问了,那么JavaScript是如何实现异步任务的呢?JavaScript不是单线程的吗?而且这篇文章不是在说事件循环,这和JS实现异步任务有什么关系呢?别着急,我们先深究一下,什么是“单线程”?常与“线程”相提并论的“进程”是什么?进程和线程的官方概念:进程(process):运行在计算机上的程序线程(thread):操作系统可以运行计算调度的最小单位,是进程中实际运行的单位。可以简单理解为每当我们在电脑中运行电脑时。一个软件程序会开启一个新的进程来运行该程序(有时会有多个进程,因为浏览器有多个进程)。其中一个进程可以有多个线程,多个线程可以在进程中并发运行,让每个线程执行不同的任务。在每个线程中,代码按单一顺序执行。单线程JavaScript那么我前面说的:JavaScript是单线程的,也就是说JavaScript只能按顺序执行代码。如果前面的代码执行时间比较长,后面的代码就只能等待,造成阻塞。想象一下,前面的代码要等几十秒才能发送请求.....后面的代码都得等...这时候你可能又要问了:为什么JavaScript要设置成单线程?一开始就设置成多线程不好吗?这与JavaScript的目的有关。作为一种浏览器脚本语言,它有一个核心目的:与用户交互和操作DOM。想象一下,如果JavaScript是多线程的,我在一个线程上往一个DOM节点上添加东西,在另一个线程上删除DOM,我应该先删除还是先添加?为了避免这种复杂情况,JavaScript只能设置为单线程。但是为了避免上面提到的阻塞现象,需要异步任务。那么回到最初的问题:那么JavaScript是如何实现异步任务的呢?提供异步能力的运行时JavaScript的异步能力实际上是由运行时环境(JavaScriptruntime)提供的。例如,JavaScript可以在chrome或node中执行,因此chrome和node都是JavaScriptRuntime。如图是一个浏览器的JavaScript运行时的简单示意图(nodejs不同),我们简单看一下这个结构:JavaScript运行时可以理解为一个进程区(线程池),主要包括JS引擎(JavaScriptEngine)以及WebAPI、回调队列(图中的任务队列和微任务队列),三者的主要功能如下(详见:https://juejin.cn/post/6844903908452597768):JS引擎:内存堆(memoryheap)分配存放引用类型实际值等的内容调用栈(callstack)解析JS源代码,转换成可执行单元(前面提到的JS代码是单线程的,也就是体现在调用栈上:所有的JS代码都在(唯一的调用栈中按顺序执行)WebAPI:上面提到的为JS本身提供异步能力的特性集就是浏览器的WebAPI。其实setTimeout、setInternal等异步API并不是js自己提供的,而是浏览器提供的。提供回调队列:该模块辅助WebAPI处理异步操作,用于保存调用过的回调。除了这三个模块之外,还有一个事件循环(Eventloop),就是这次的主角。它负责JS的异步机制和“检察官”说到这里,我们还不知道javascript运行时中的事件循环是如何为我们提供异步能力的。下面我们来深挖一下浏览器的事件循环~(在node环境是不一样的)浏览器的事件循环先来看看eventloop,它是javascript运行时的调整机制。它的主要功能是:Eventloop的工作是lookintothecallstackanddeterminingifthecallstackisemptyornot(查看调用堆栈并确定其当前状态是否为空)。如果调用堆栈为空,它会查看消息队列,看是否有任何等待执行的挂起回调(如果调用堆栈为空,它会去查看消息队列/回调队列有回调等待执行)我们来看看JavaScript中同步任务和异步任务的执行规则。同步和异步任务进入不同的执行“位置”:要执行的同步任务会在调用栈(callstack)中排队执行:只有上一个任务执行完了,才能执行下一个任务。异步任务不会进入主线程,而是在得到运行结果后,在任务队列(taskqueue)中放置一个事件。如果调用栈中的所有同步任务都执行完成后,系统会去任务队列中查看还有哪些异步任务需要执行,将任务队列中完成的异步任务放入调用栈中,并开始以连续循环的方式执行以上三个步骤,所以就变成了“事件循环”结合了事件循环和异步任务执行规则。是不是好像事情清楚了很多?但这还没有结束,让我们仔细看看我们的任务队列浏览器的宏任务和微任务。其实任务队列又细分为microtask队列和macrotask队列。不同类型的异步任务事件会分别进入不同的任务队列,不同的任务队列有不同的执行规则宏任务一般包括:ajax、setTimeout、setInterval、DOM监听、UI渲染等。微任务一般包括:Promisethencallback、queueMicrotask()等。两个队列的执行规则如下:当调用栈是为空,系统检查任务队列时,会先查看当前的microtask队列。如果当前微任务队列中有事件,它会先执行队列中的所有事件,然后再执行当前宏任务队列中的事件。执行完一个事件后,查看这个是否有新的microtask事件,如果有的话,只有microtask事件执行完后,才能执行下一个macrotask。。。这样一个循环说到这里,你也应该知道它们之间的关系了JS对异步和事件循环的实现,以及事件循环是如何实现的?如果有写的不准确的地方,请大家提出来(ps:其实nodejs中的事件循环和浏览器有些区别,有空再写一篇~)文章参考:https://www.youtube.com/watch?v=8aGhZQkoFbQhttps://juejin.cn/post/7083286147920560158https://www.ruanyifeng.com/blog/2014/10/event-loop.htmlhttps://dev.to/lydiahallie/javascript-可视化事件循环3dif