返回
JS 中的微任务与宏任务:揭开它们的神秘面纱
前端
2024-01-11 19:48:01
面试官们喜爱出一些棘手的问题来考察候选人的前端基础知识,而关于 JS 中微任务和宏任务的问题就是其中之一。深入了解这些概念不仅对于理解 JS 运行时至关重要,而且对于解决棘手的编码谜题和优化代码性能也大有裨益。
本文将带你踏上一段探险之旅,深入了解 JS 中微任务和宏任务的微妙世界。我们将探讨这些概念的基本原理,它们之间的相互作用,以及它们对我们编写健壮且高效代码的影响。
微任务与宏任务:一个直观的类比
想象一家繁忙的餐厅,其中主线程就像一位忙碌的厨师,负责准备食物。微任务和宏任务就像等待就餐的顾客。
- 微任务: 就像那些耐心等待着食物的VIP顾客。他们会优先处理,在厨师完成主餐之前就得到服务。
- 宏任务: 就像那些愿意等待的普通顾客。他们会在微任务都得到处理后才轮到就餐。
微任务的起源
微任务是 JavaScript 中的一种特殊任务类型,它们起源于以下异步操作:
- Promise 的解析
- MutationObserver 回调
- requestAnimationFrame 回调
当这些操作完成时,它们会将相应的微任务添加到事件队列中。
宏任务的起源
宏任务是另一种 JavaScript 任务类型,它们起源于以下操作:
- setTimeout 和 setInterval 回调
- DOM 事件处理程序(例如 click、scroll)
- XMLHttpRequest 请求
与微任务不同,宏任务会在主线程中排队,只有在前一个宏任务完成后才会执行。
微任务和宏任务的执行顺序
JS 事件队列遵循以下执行顺序:
- 微任务: 所有微任务都会在宏任务之前执行。
- 宏任务: 所有宏任务都会按照先入先出的顺序执行。
这意味着,即使宏任务先进入队列,它也必须等待所有微任务执行完毕后才能运行。
实际应用:一个面试题示例
头条的一道面试题中给出了以下代码片段:
setTimeout(() => {
console.log('宏任务 1');
}, 0);
Promise.resolve().then(() => {
console.log('微任务 1');
});
console.log('主线程');
Promise.resolve().then(() => {
console.log('微任务 2');
});
setTimeout(() => {
console.log('宏任务 2');
}, 0);
按照微任务和宏任务的执行顺序,该代码的输出为:
主线程
微任务 1
宏任务 1
微任务 2
宏任务 2
优化技巧
理解微任务和宏任务的概念对于优化代码性能至关重要。以下是一些技巧:
- 优先使用微任务: 由于微任务比宏任务优先执行,因此将耗时的操作放入 Promise 或 MutationObserver 中可以提高响应性。
- 谨慎使用宏任务: 虽然宏任务对于处理不紧急的任务很有用,但过度使用它们会阻塞主线程,从而导致性能下降。
- 利用队列微任务: 通过将微任务队列化,你可以防止大量微任务同时执行,从而避免过度消耗资源。
总结
掌握 JS 中微任务和宏任务的概念是编写健壮且高效代码的关键。通过优先使用微任务,谨慎使用宏任务,并利用队列微任务,你可以优化代码性能,解决棘手的编码谜题,并在面试中展现你的前端功底。