宝哥软件园

JS浏览器事件循环机制详解

编辑:宝哥软件园 来源:互联网 时间:2021-08-26

先了解一些概念。

进程、线程

进程是系统分配的独立资源,是CPU资源分配的基本单位。进程由一个或多个线程组成。

线程是一个进程的执行流程,是CPU调度和调度的基本单元。同一进程中的多个线程共享该进程的资源。

渲染引擎

浏览器是多进程的,浏览器的每个标签都代表一个独立的进程(不一定,因为多个空白标签会合并成一个进程),浏览器内核(浏览器渲染进程)属于浏览器多进程之一。

浏览器内核中有很多线程在工作。

图形用户界面渲染线程:

负责渲染页面,解析HTML,CSS形成DOM树等。当页面由于某种操作被重绘或回流时,这个线程将被启动。而当JS引擎线程工作时,GUI渲染线程将被挂起,GUI更新将被放入JS任务队列,等待JS引擎线程空闲时继续执行。JS引擎线程:

单线程工作,负责解析和运行JavaScript脚本。而且GUI渲染线程是互斥的,所以JS运行时间太长,会导致页面阻塞。事件触发线程:

当事件满足触发条件并被触发时,线程会将相应的事件回调函数添加到任务队列的末尾,等待JS引擎处理。

定时器触发线程:

浏览器计时计数器不被JS引擎计数,阻塞会导致计时不准确。启动计时器,触发线程计时并触发计时。计时完成后,会添加到任务队列中,等待JS引擎处理。Http请求线程:

当http请求时,会打开一个请求线程。请求完成后,将请求的回调函数添加到任务队列中,等待JS引擎处理。

JavaScript引擎是单线程的

JavaScript引擎是单线程的,这意味着一次只能执行一个任务,其他任务必须排队才能执行,下一个任务只有在当前任务执行完之后才会执行。

Web-Worker API是在HTML5中提出的,主要是为了解决页面阻塞的问题,但并没有改变JavaScript是单线程的本质。了解网络工作者。

JavaScript事件循环机制

JavaScript事件循环机制分为浏览器和Node事件循环机制,其实现技术各不相同。浏览器事件循环是HTML中定义的标准,节点事件循环由libuv库实现。这主要是关于浏览器部分。

Javascript有一个主线程和一个调用栈调用栈(执行栈),所有任务都放在调用栈上等待主线程执行。

JS调用栈

JS调用栈是一种后进先出的数据结构。当一个函数被调用时,它将被添加到堆栈的顶部。执行后,该函数将从堆栈顶部移除,直到堆栈被清空。

同步任务、异步任务

JavaScript单线程中的任务分为同步任务和异步任务。同步任务会在调用栈中排队,以便主线程执行,而异步任务会在获得异步结果后,将注册的回调函数添加到任务队列(消息队列)中,当主线程空闲时,也就是栈被清空时,它会被读入栈中供主线程执行。任务队列是一种先进先出的数据结构。

事件循环

当调用堆栈中的所有同步任务都被执行并且堆栈被清空时,这意味着主线程是空闲的。此时,任务将按顺序从任务队列中读取,并放入堆栈中执行。每次堆栈清空时,它都会读取任务队列中是否有任务。如果有任务,就会读取并执行,读-执行操作会一直循环,从而形成一个事件循环。

计时器

计时器将启动一个计时器触发线程来触发计时。等待指定时间后,定时器会将事件放入任务队列,等待主线程执行读取。

实际上,定时器指定的延迟毫秒数并不准确,因为定时器只有在指定时间到达时才会将事件放入任务队列,需要等到同步的任务和现有任务队列中的事件全部执行完毕后,才能将定时器的事件读取到主线程中执行。中间可能会有耗时较长的任务,无法保证在指定时间执行。

宏观任务和微观任务

除了广义的同步任务和异步任务之外,JavaScript单线程中的任务还可以细分为宏任务和微任务。

宏任务包括:脚本(全代码)、settimeout、setinterval、setimmediate、I/o、uirendering。

微观任务包括:过程。下一个记号,承诺,反对。观察,和突变观察者。

console . log(1);setTimeout(function(){ console . log(2);})var promise=new Promise(函数(解析,拒绝){ console . log(3);resolve();})promise . then(function(){ console . log(4);})console . log(5);在示例中,setTimeout和Promise被称为任务源,从不同任务源注册的回调函数将被放入不同的任务队列中。

有了宏任务和微任务的概念,JS的执行顺序是什么?宏任务优先还是微任务优先?

在第一个事件循环中,JavaScript引擎将整个脚本代码作为一个宏任务来执行。执行完成后,它将检测在这个循环中是否发现微任务。如果它们存在,它将依次从微任务的任务队列中读取并执行所有微任务,然后读取宏任务的任务队列中的任务执行,然后执行所有微任务,以此类推。JS的执行顺序是每个事件周期中的宏任务-微任务。

在上例中,在第一个事件循环中,整个代码作为宏任务进入主线程执行。遇到setTimeout时,回调函数会在指定时间过去后放入宏任务的任务队列中。遇到Promise时,将then函数放入微任务的任务队列中。整个事件周期完成后,它会检测微任务的任务队列中是否有任务,如果有就执行。第一个循环结果打印为: 1,3,5,4。然后,按顺序从宏任务队列中取出一个宏任务,并将其放在堆栈上,供主线程执行。那么这个周期的宏任务就是setTimeout注册的回调函数。执行完这个回调函数,发现这个周期没有微任务,然后准备下一个事件周期。如果检测到宏任务队列中没有要执行的任务,则事件循环结束。最后的结果是1,3,5,4,2。以上是边肖介绍的JS浏览器事件流转机制的详细讲解和整合,希望对大家有所帮助。如果你有任何问题,请给我留言,边肖会及时回复你。非常感谢您对我们网站的支持!

更多资讯
游戏推荐
更多+