【前端面经】JS 中的事件循环机制是什么?

JS 的事件循环机制

调用栈、宏任务、微任务和事件循环协同工作流程:同步任务首先执行,接着执行微任务队列中的所有任务,最后执行宏任务队列中的任务。通过这种机制,JavaScript 能够有效地处理异步任务,并确保它们在正确的时机执行。

调用栈(Call Stack)

介绍

调用栈是一个后进先出(LIFO, Last-In-First-Out)的栈结构,用于管理函数调用和执行上下文。当 JavaScript 引擎执行代码时,会创建执行上下文并将其推入调用栈。函数调用完成后,对应的执行上下文会从调用栈中弹出

执行上下文

执行上下文是 JavaScript 执行代码时的环境,包含了代码执行所需的所有信息。每个执行上下文都有三个主要部分:

  • 变量对象(Variable Object,VO):包含了变量、函数声明和函数参数。
  • 作用域链(Scope Chain):当前执行上下文及其父级执行上下文的变量对象的列表,用于标识标识符的可见性和可访问性。(当在执行上下文中访问一个变量时,JavaScript 引擎会按照作用域链进行查找。
  • this 绑定:指向执行上下文中的 this 对象。
全局执行上下文

全局执行上下文是 JavaScript 代码启动时创建的第一个执行上下文,它包含全局作用域。全局执行上下文有以下特点:

  • 变量对象:在全局执行上下文中,变量对象是全局对象(如 window 对象在浏览器中)。
  • 作用域链:全局执行上下文的作用域链仅包含全局变量对象。
  • this 绑定:在全局执行上下文中,this 绑定到全局对象(如浏览器中的 window 对象)。
函数执行上下文

函数执行上下文:每当函数被调用时,JavaScript 会创建一个新的执行上下文,该执行上下文有其自己的变量对象、作用域链和 this 绑定。函数执行上下文的作用域链是以其自身的变量对象为起点,然后是父级执行上下文的变量对象,最终指向全局执行上下文的变量对象

宏任务与微任务

宏任务

宏任务(Macro Task):宏任务是由浏览器提供的任务,包括以下内容。宏任务被放入宏任务队列中,事件循环会依次取出宏任务执行。

  • setTimeout、setInterval 等定时器回调。

  • DOM 事件回调,如 click、load、change 等。

  • 网络请求回调,如 XMLHttpRequest、fetch 等。

  • I/O 操作,如文件读写。

  • 其他由浏览器调度的任务,如渲染事件。

微任务

微任务(Micro Task):微任务是由 JavaScript 引擎提供的任务,主要包括以下内容。微任务被放入微任务队列中,微任务队列在当前宏任务执行结束后、下一个宏任务执行之前执行。

  • Promise 的回调函数(.then()、.catch()、.finally())。

  • async/await 产生的任务。

  • MutationObserver 的回调函数。(MutationObserver 是一种在现代浏览器中提供的接口,用于监视 DOM 树中发生的变化,并在检测到变化时异步地通知用户。它可以用来观察 DOM 节点的添加、删除、属性变化、文本内容变化等情况。)

事件循环(Event Loop)

事件循环:事件循环是 JavaScript 引擎用于管理异步任务执行顺序的机制。其工作流程如下:

  • 执行同步任务:从调用栈顶开始执行同步代码,直到调用栈为空
  • 执行微任务:检查并执行微任务队列中的所有微任务直到微任务队列为空
  • 更新渲染:如果需要,更新页面渲染
  • 执行宏任务:从宏任务队列中取出下一个宏任务执行,然后重复步骤 1。

事件循环代码示例

  1. 代码示例
    	console.log('Start'); // 同步代码
    
    setTimeout(() => { // 宏任务
      console.log('Timeout 1');
    }, 0);
    
    Promise.resolve().then(() => { // 微任务
      console.log('Promise 1');
    }).then(() => {
      console.log('Promise 2');
    });
    
    setTimeout(() => { // 宏任务
      console.log('Timeout 2');
    }, 0);
    
    console.log('End'); // 同步代码
    
  2. 输出顺序
    Start
    End
    Promise 1
    Promise 2
    Timeout 1
    Timeout 2
    
  3. 执行步骤解析
    • 同步任务 (1st)

      • console.log('Start') 被推入调用栈并执行,输出 ‘Start’,然后从调用栈弹出。
      • setTimeout 设置两个定时器,将它们的回调函数添加到宏任务队列中。
      • Promise.resolve() 将第一个 .then() 回调添加到微任务队列中。
      • console.log('End') 被推入调用栈并执行,输出 ‘End’,然后从调用栈弹出。
    • 执行微任务 (2nd)

      • 微任务队列中的第一个 Promise 回调被推入调用栈并执行,输出 ‘Promise 1’,然后从调用栈弹出。
      • 第二个 .then() 回调添加到微任务队列中。
      • 微任务队列中的第二个 Promise 回调被推入调用栈并执行,输出 ‘Promise 2’,然后从调用栈弹出。
    • 执行宏任务 (3nd)

      • 宏任务队列中的第一个 setTimeout 回调被推入调用栈并执行,输出 ‘Timeout 1’,然后从调用栈弹出。
      • 宏任务队列中的第二个 setTimeout 回调被推入调用栈并执行,输出 ‘Timeout 2’,然后从调用栈弹出。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值