微任务回调复用定义时的执行上下文,不创建新上下文。其变量对象、作用域链、this值均沿用原始环境,闭包完整保留,调用不压入新栈帧,与宏任务新建上下文有本质区别。

微任务执行时,代码运行在它被创建时所处的原始执行上下文中,不是新创建的独立上下文。也就是说,Promise.then、MutationObserver 或 queueMicrotask 回调函数执行时,其变量对象、作用域链和 this 值都沿用定义它们时所在函数或全局环境的上下文,不会因为“异步”而丢失闭包或改变绑定。
微任务回调共享定义时的执行上下文
微任务本身不触发新的执行上下文创建。它只是把回调函数推入微任务队列,在当前宏任务结束、同步代码清空后,立即按先进先出顺序取出并执行——但执行时仍使用原有上下文。
- 比如在函数
f()内声明Promise.resolve().then(() => console.log(x)),其中x是f的局部变量:回调执行时,x依然可通过作用域链访问,因为该回调闭包捕获的是f的函数执行上下文。 -
this值也不会重置:箭头函数保持父级上下文的this;普通函数若未显式绑定,则在非严格模式下指向全局对象(浏览器中为window),严格模式下为undefined——这与定义位置一致,而非执行时机。 - 变量对象(VO)不重建:微任务回调中访问的变量,查找路径仍是「当前上下文 VO → 外层函数 VO → 全局 VO」,和同步执行时完全一致。
微任务执行不打断当前调用栈
微任务队列清空发生在宏任务结束、控制权交还事件循环前,此时函数调用栈已完全弹出(所有同步函数执行完毕),但微任务回调的执行**不压入新栈帧**——它们是作为普通函数调用直接执行的,只是时机特殊。
- 例如:
setTimeout(() => {}, 0)的回调会新建一个宏任务上下文;而Promise.then(() => {})的回调复用原上下文,不产生额外栈帧。 - 调试时能看到:微任务回调的调用栈顶部是
Promise.then或queueMicrotask,但其内部函数的[[Environment]]指向定义时的词法环境,不是事件循环环境。
注意嵌套微任务的上下文继承
在微任务中再创建微任务(如 Promise.then(() => Promise.resolve().then(...))),新微任务的回调仍继承外层微任务执行时的上下文——也就是最初定义它的那个环境,而不是“微任务执行环境”本身(JavaScript 中并不存在所谓“微任务上下文”这一概念)。
- 这意味着闭包链完整保留:内层
then仍能访问外层函数的所有自由变量。 - 也意味着错误堆栈可追溯:抛出异常时,堆栈信息包含原始定义位置,而非微任务调度点。
- 但要注意:如果微任务中修改了外部变量,后续微任务会看到更新后的值——因为它们共享同一变量对象,不是快照。
与宏任务上下文的关键区别
宏任务(如 setTimeout、setInterval)每次执行都会创建全新执行上下文;微任务则始终复用已有上下文。这是两者在执行模型上的根本差异,直接影响闭包行为、this 绑定和内存生命周期。
- 宏任务回调:进入新函数上下文,
this默认为全局对象(非严格),作用域链从当前宏任务开始构建。 - 微任务回调:无新上下文,仅是已有上下文中的函数调用,语义等价于同步调用,只是延迟执行。
- 因此,微任务更适合需要保持上下文连贯性的场景,比如状态更新链、响应式依赖收集、DOM 批量变更等。











