MST

星途 面试题库

面试题:JavaScript事件循环与异步操作的深入理解

假设你有一段代码包含多个异步操作,如setTimeout、Promise、async/await,解释在事件循环机制下它们的执行流程,同时说明如果有多个微任务和宏任务嵌套,如何准确预测最终的输出结果。
32.4万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试

事件循环机制下异步操作执行流程

  1. 宏任务队列与微任务队列:JavaScript 运行环境有一个事件循环(Event Loop),它会不断检查调用栈是否为空。当调用栈为空时,会从宏任务队列中取出一个任务放入调用栈执行。宏任务包括 setTimeoutsetIntervalsetImmediate(Node.js 环境)、I/O 操作、UI 渲染等。而微任务队列会在当前宏任务执行完,且调用栈再次为空时,被事件循环处理。微任务包括 Promise.thenprocess.nextTick(Node.js 环境)、MutationObserver 等。
  2. setTimeout 执行流程setTimeout 会将其回调函数放入宏任务队列。当设定的延迟时间到达后,回调函数就会进入宏任务队列等待执行。只有当调用栈为空,事件循环从宏任务队列中取出该任务放入调用栈,才会执行 setTimeout 的回调。
  3. Promise 执行流程Promise 构造函数内的代码是同步执行的。当 Promise 状态改变(resolvereject)时,.then.catch 中的回调函数会被放入微任务队列。在当前宏任务执行完,调用栈为空时,事件循环会处理微任务队列,依次执行这些微任务。
  4. async/await 执行流程async 函数返回一个 Promiseawait 关键字只能在 async 函数内部使用,它会暂停 async 函数的执行,等待 Promise 被解决(resolvedrejected)。await 后的 Promise 解决后,await 表达式的值就是该 Promise 的解决值。await 之后的代码会被包装成微任务放入微任务队列。

多个微任务和宏任务嵌套时预测输出结果

  1. 宏任务优先:首先执行同步代码,同步代码执行完后,事件循环开始处理宏任务队列。每次从宏任务队列取出一个宏任务执行,直到宏任务执行完且调用栈为空。
  2. 微任务紧跟宏任务:在一个宏任务执行完且调用栈为空后,事件循环会处理微任务队列,将微任务队列中的所有任务依次执行完,才会再次从宏任务队列中取出任务。
  3. 嵌套情况分析
    • 当遇到多层 setTimeout 嵌套时,它们会按照设定的延迟时间依次进入宏任务队列。较早到期的 setTimeout 回调先进入宏任务队列,先被执行。
    • 对于 Promise 嵌套,如果内层 Promise.then 回调中又创建了新的 Promise,新的 Promise.then 回调依然会被放入微任务队列。但要注意,外层 Promise.then 回调会在内层 Promise 解决后,先于新创建 Promise.then 回调执行(因为微任务队列是先进先出)。
    • async/await 嵌套中,内层 async 函数中的 await 会暂停内层函数执行,外层 async 函数也会暂停,直到内层 awaitPromise 解决。内层 await 后的代码作为微任务处理,外层 await 后的代码同样作为微任务,且外层 await 后的微任务会在内层 await 后的微任务执行完后执行(因为微任务队列先进先出)。

通过明确宏任务和微任务的执行顺序,以及各种异步操作在事件循环中的处理方式,就可以准确预测最终的输出结果。