随着浏览器应用的不断发展,我们在处理任务的方式上也经历了一些变革。传统的消息队列机制在某些领域已经不能满足实时性和效率的需求,因此出现了微任务的概念,使我们能够更灵活地在实时性和效率之间取得平衡。
1.宏任务
在浏览器中,大部分任务都是在主线程上执行的,这包括了渲染事件、用户交互事件、JavaScript
脚本执行事件、网络请求完成、文件读写完成等。为了协调这些任务的执行,浏览器引入了消息队列和事件循环机制。渲染进程内部维护多个消息队列,如延迟队列、普通消息队列等。主线程通过一个 for
循环不断从这些任务队列中取出任务并执行。
消息队列中的任务被称为宏任务,其执行过程大致分为以下步骤:
- 先从消息队列中取出一个最老的任务,称为`oldestTask。
- 然后循环系统记录任务开始执行的时间,并把这个
oldestTask
设置为当前正在执行的任务。 - 当任务执行完成之后,删除当前正在执行的任务,并从对应的消息队列中删掉这个
oldestTask
。 - 最后统计执行完成的时长等信息。
尽管宏任务是一种可靠的执行方式,但对于对时间精度有更高要求的场景,宏任务的粗时间颗粒度就显得不够灵活。
2.微任务
为了解决宏任务的时间颗粒度问题,引入了微任务的概念。微任务是一种需要异步执行的函数,其执行时机是在主函数执行结束之后、当前宏任务结束之前。 每个宏任务都会关联一个微任务队列 ,而在 JavaScript
执行脚本时,V8 引擎会在全局执行上下文中创建一个微任务队列。
微任务产生的时机和执行微任务的时机
微任务产生的时机
- 使用
MutationObserver
监控某个DOM
节点,然后通过JavaScript
来修改这个节点,或者为这个节点添加、删除子节点,当DOM
节点发生变化时,就会产生DOM
变化记录的微任务。 - 使用
promise
,当调用Promise.resolve()
或者Promise.reject()
的时候也会产生微任务。
执行微任务的时机
微任务的执行时机通常在当前宏任务中的 JavaScript
快执行完成时,这个时刻被称为微任务检查点 。在检查点时,JavaScript
引擎会检查全局执行上下文中的微任务队列,并按照顺序执行队列中的微任务。值得注意的是,如果在执行微任务的过程中产生了新的微任务,它将被添加到微任务队列中,并在当前宏任务中继续执行,而不会推迟到下个宏任务。
3.监听DOM变化方法的演变
在Web
开发中,监听DOM
变化的方法经历了一些演变。以下是一些常用的监听 DOM
变化的方法及其演变:
1. 轮询(Polling):
早期的 DOM
监听方式是通过轮询检查 DOM
是否发生变化。通过定期检查 DOM
结构的变化,然后执行相应的操作。这种方法的缺点是效率低下,不够实时,同时可能会造成性能问题。
js
setInterval(function () {
// 检查 DOM 变化
// 执行相应操作
}, 1000); // 每秒轮询一次
2. 事件监听器:
随着浏览器的发展,引入了更为实时的事件监听器,允许开发者监听 DOM
元素的特定事件,从而实时获取变化。
js
document.addEventListener('change', function (event) {
// 处理 DOM 变化事件
});
3. MutationObserver:
MutationObserver
是一个现代的、高效的 API
,用于异步监听DOM
的变化。它能够捕获到DOM
树的变化,包括节点的增加、删除、属性的变化等。相对于事件监听器,MutationObserver
提供了更为灵活和强大的功能。
js
// 创建一个 MutationObserver 实例
const observer = new MutationObserver(function (mutationsList, observer) {
// 处理 DOM 变化
});
// 配置监听选项
const config = { attributes: true, childList: true, subtree: true };
// 开始监听
observer.observe(document.body, config);
MutationObserver
的优势在于它不会阻塞主线程,而是通过异步方式捕获 DOM
变化,因此更适用于大规模的、频繁的 DOM
操作。
4.性能优化建议
微任务的合理使用
在使用微任务时,需要谨慎并避免过度依赖微任务来处理任务。过多的微任务可能导致性能问题,因此应该合理权衡宏任务和微任务的使用,确保在不牺牲实时性的前提下提高效率。
utationObserver 的异步特性
开发者需要注意 MutationObserver
具有异步特性,不会阻塞主线程。这使得 MutationObserver
更适用于大规模、频繁的 DOM
操作,能够减少页面的卡顿和性能问题。