likes
comments
collection
share

Vue异步队列:微任务vs宏任务

作者站长头像
站长
· 阅读数 4

大家好,我是拾七,持续给大家补给前端干货🔥

在JavaScript的异步编程模型中,任务被分为两大类:宏任务和微任务。它们之间的主要区别在于执行的时机与调用栈的关系。宏任务会在当前执行栈清空后执行,而微任务则会在当前执行栈执行完毕后立即执行。

  • 微任务队列:微任务队列中的任务会在当前任务执行完成之后、下一个事件循环的开始之前执行。微任务包括 Promise 回调、MutationObserver 回调和 process.nextTick(Node.js 中)等。 
  • 宏任务队列:宏任务队列中的任务会在当前事件循环的所有微任务执行完毕之后执行。宏任务包括 setTimeout、setInterval、requestAnimationFrame、I/O 操作等。

Vue.js选择将其异步队列放在微任务队列而非宏任务队列,这背后的原因涉及到JavaScript的事件循环、执行上下文以及性能优化等方面。

我们需要理解事件循环机制。在JavaScript中,事件循环是协调JavaScript单线程执行环境和处理异步操作的机制。JavaScript引擎在执行代码时会维护一个执行栈来记录当前执行的上下文。当遇到异步代码时,比如setTimeout和setInterval等宏任务或者Promise、process.nextTick等微任务,它们会被分别放置到对应的任务队列中等待执行。

当执行栈中的同步任务执行完毕,JavaScript引擎会从任务队列中取出排在最前的任务并放入执行栈执行。对于宏任务,它会在所有的微任务都执行完毕后才被执行;而对于微任务,它会在当前执行栈清空之后,下一次事件循环之前被执行。这样的设计使得微任务相对于宏任务有着更高的优先级,可以确保相关的回调函数尽可能早地被触发。

为什么Vue的异步队列放在微任务队列呢?这主要是因为Vue的响应式系统和组件更新机制的设计需要。Vue的响应式系统依赖于对象的getter和setter,当数据发生变化时,setter会被触发,然后进行依赖的收集,最终将这个变化放入异步队列等待更新视图。这个过程需要尽可能快地进行,以便能够实时反映数据的变化。

如果这些更新操作被放入宏任务队列,那么它们可能需要等待所有的微任务以及其他宏任务执行完毕才能被执行,这将导致视图的更新不够及时,影响用户的体验。而如果这些更新操作被放入微任务队列,那么它们可以在当前执行栈清空之后立即得到执行,这样可以保证视图的更新尽可能地实时。

将更新操作放入微任务队列还可以避免一些潜在的性能问题。例如,如果在宏任务中进行了数据更新,并且这个更新又触发了新的数据更新,那么就可能导致大量的宏任务被添加到任务队列中,从而导致“任务队列爆炸”。而如果这些更新操作被放入微任务队列,那么由于微任务的优先性高于宏任务,这样就可以避免这种情况的发生。

基于以上的考虑,Vue选择了将其异步队列放在微任务队列而非宏任务队列。这样的设计既保证了视图的实时更新,又避免了可能的性能问题,从而提高了Vue应用的性能和用户体验。

总结

Vue 将异步更新队列放在微任务队列中的主要原因有以下几点:

    1. 更快的响应性:将异步更新队列放在微任务队列中可以尽可能快地响应状态的变化。因为微任务队列中的任务会在当前任务执行完成后立即执行,这样可以确保状态的更新尽快被应用到视图上,提高了页面的响应性和用户体验。
    1. 更高的性能:微任务的执行优先级比宏任务高,它们会在每个事件循环的开始之前执行,而宏任务则会在当前事件循环的末尾执行。因此,将异步更新放在微任务队列中可以尽可能地减少页面重绘的次数,提高了性能和效率。
    1. 避免可能的视图闪烁:如果异步更新放在宏任务队列中,可能会导致视图的更新延迟到下一个事件循环开始时才进行,这可能会导致页面出现视觉上的闪烁或不稳定性。通过放在微任务队列中,可以尽可能地避免这种情况的发生。