JavaScript 宏任务和微任务区别
你或许曾遇到过这样的场景:打开一个网页,明明按钮已经点了,却要等图片加载完才弹出提示;又或者打字时,输入的字符总是慢半拍才显示。这些“等待”背后,藏着JavaScript的任务调度逻辑——宏任务和微任务。很多人刚接触这部分时,总觉得像绕口令,其实用生活里的“快递流程”就能讲明白。
什么是JavaScript的任务队列?
JavaScript是单线程运行的,就像一家只有一个快递员的小店,同一时间只能处理一件事。为了不浪费时间,它会把暂时不能立刻处理的事,先放到“待办清单”(也就是任务队列)里,等手头的事做完,再按顺序取新任务处理。这个待办清单分两类,对应宏任务和微任务,就像快递里的“普通包裹”和“加急件”。

宏任务:按部就班的普通包裹
宏任务是JavaScript最常见的任务类型,就像平时寄的普通包裹,处理优先级低,要等当前所有正在做的事都完成,才会从队列里取出来处理。生活里的例子很好懂:你先在家整理衣服(当前主线程任务),然后下单寄普通快递(宏任务),快递员要等你整理完衣服才来取件。
常见的宏任务有哪些?比如网页的点击事件、定时器setTimeout、资源加载事件(像图片加载完成),甚至是Ajax请求的回调,都属于宏任务。举个实际的小例子:你写了一段代码,点击按钮后弹出提示,这个点击事件的回调就是宏任务——得等页面加载、其他同步代码跑完,用户点了按钮,才会把这个宏任务放进队列,之后再执行。
微任务:优先处理的加急件
微任务是优先级极高的任务,就像你寄的“当日达”加急包裹,只要手头的事一有空,不管之前排了多少普通包裹,都会先把加急件取出来处理。用快递的例子再拆解:同样是整理完衣服,快递员先接当日达的加急件,再去取之前排的普通包裹。
微任务的出现,是为了避免普通任务“堵路”,让需要立刻处理的逻辑(比如更新页面状态、处理用户输入)更快执行。常见的微任务有:Promise的then回调(注意,Promise本身的 executor 是同步的,只有then的回调属于微任务)、async/await里的await部分、MutationObserver(监听DOM变化的API)的回调。
最核心的区别:处理时机和优先级
很多人分不清两者,其实只要记住两个关键点就不会错:
第一,优先级不同。宏任务是队列里的“普通件”,微任务是“加急件”,每次主线程空了,会先把当前所有微任务清空,再去取第一个宏任务执行。比如你写了这样一段代码:
`js
console.log('主线程任务1');
setTimeout(() => console.log('宏任务执行'), 0);
Promise.resolve().then(() => console.log('微任务执行'));
console.log('主线程任务2');
`
运行后的输出顺序是:主线程任务1 → 主线程任务2 → 微任务执行 → 宏任务执行。这就是因为微任务比宏任务先处理,哪怕setTimeout设了0延迟,它的回调还是要等微任务处理完才执行。
第二,来源不同。宏任务是外部触发的常规操作,微任务是为了优化执行效率而生的“特殊回调”,两者的设计初衷完全不同——一个是日常调度,一个是紧急处理。
搞懂宏任务和微任务的区别,能帮你轻松解决网页卡顿、逻辑顺序错乱的问题,比如遇到输入延迟,就知道是不是微任务没及时处理;遇到点击无响应,也能排查是不是宏任务队列太长,阻塞了主线程。