前端面试题库

浏览器的事件循环机制

事件循环更新时间:2024-08-24 14:16:53

答案

JavaScript 有一个基于事件循环的并发模型,事件循环负责执行代码、收集和处理事件以及执行队列中的子任务。

讨论事件循环之前先想一下js为什么是单线程、为什么需要异步、以及异步是怎么实现的。

  1. 假如有两个js线程process1process2process1删除dom1,而与此同时process2在编辑dom1,导致操作冲突,所以js被设计为了单线程。
  2. 假如在执行js代码时,某一行耗时很长,后面的代码就会被阻塞,同时用户的操作也没反应,出现假死现象;所以可以先处理紧急/优先级高的事情,优先级低/耗时长的任务放后面,所以就需要异步。
  3. 有了异步任务后,就需要在恰当的时机执行它,所以就有了事件循环。

下面这张图展示了跟事件循环有关的几个概念:

https://file.vwood.xyz/2024/08/24/upload_k7ax88fnwr1pj43cdb7znqbapgf6jr9t.png

函数调用形成了一个由若干帧组成的栈。

function foo(b) { let a = 10; return a + b + 11; } function bar(x) { let y = 3; return foo(x * y); } console.log(bar(7)); // 返回 42

当调用bar时,第一个帧被创建并压入栈中,帧中包含了bar的参数和局部变量,当执行到foo时,第二个帧创建并被压入栈中,包含foo的参数和局部变量;当foo执行完后,第二帧就被弹出,当bar返回后,第一帧也被弹出;此时栈被清空。

js中对象被分配在堆中

队列

javascript中包含了一个待处理消息的消息队列,每一个消息都关联着一个用于处理这个消息的回调函数。

事件循环时每次都会从最先进入队列的消息开始处理,被处理的消息会被移除队列,并作为输入参数来调用与之关联的函数;通过上面的可知,每次调用函数都会创建新的栈帧,每次执行完执行栈都会清空。

在执行js脚本时,普通函数会放入栈中正常执行即可,异步任务会被加入到任务队列中(宏任务队列或微任务队列)

宏任务和微任务

宏任务: script、setTimeout、setInterval、IO操作、postMessage、UI交互

微任务:Promise、MutationObserve、queueMicrotask

事件循环

javascript会等待并循环执行消息队列任务,每个函数执行时,它不会被抢占,只有它执行完毕后才会允许执行其他任何代码,新的消息会被添加到消息队列。

任务执行过程:

  1. 浏览器是先执行完一个宏任务(没有就直接执行微任务),然后执行所有的微任务;

  2. 微任务执行过程中如果遇到微任务,就加入微任务队列中并在本次事件循环中执行;

  3. 遇到宏任务就将其加入到宏任务队列中;

  4. 浏览器渲染ui;

  5. 执行下一个宏任务;

评论