附:认识queueMicrotask方法 及 MutationObserver接口
一、将微任务加入微队列
Window:queueMicrotask() 方法 - Web API | MDN
window.
queueMicrotask(callback)
方法【或者 Web Worker 中 WorkerGlobalScope接口的queueMicrotask(callback)
方法】
二、MutationObserver 监视DOM 树的更改并执行回调
MutationObserver.takeRecords() - Web API | MDN
var observer = new MutationObserver(callback);实例方法:
observer.observe(target[, options])
方法 开始监听与给定选项匹配的DOM变化。
observer.takeRecords()
方法返回已检测到但尚未由观察者的回调函数处理的所有匹配 DOM 更改的列表,使变更队列保持为空。 (可见:通常disconnect之前需要执行takeRecords)
observer.disconnect()
方法告诉观察者停止观察变动
示例 :
//回调
function callback(mutationList, observer) {
mutationList.forEach((mutation) => {
switch (mutation.type) {
case "childList":
/* 从树上添加或移除一个或更多的子节点;参见 mutation.addedNodes 与
mutation.removedNodes */
break;
case "attributes":
/* mutation.target 中某节点的一个属性值被更改;该属性名称在 mutation.attributeName 中,
该属性之前的值为 mutation.oldValue */
break;
}
});
}
var targetNode = document.querySelector("#someElement");
var observerOptions = {
childList: true,
attributes: true,
};
var observer = new MutationObserver(callback);
observer.observe(targetNode, observerOptions);
/* ...later, when it's time to stop observing... */
/* handle any still-pending mutations */
//通过调用 takeRecords() 来处理任何未传递的 MutationRecord
var mutations = observer.takeRecords();
if (mutations) {
callback(mutations);
}
observer.disconnect();
深入理解JavaScript:宏队列与微队列的区别
(转自:https://wenku.csdn.net/doc/2fk628h3v9)
宏队列用于管理诸如setTimeout、setInterval、I/O、UI Rendering等任务,而微队列则用于管理Promise的回调函数、MutationObserver等。当一个宏任务执行完毕,事件循环会先查看微队列中是否有任务,如果有,则执行所有微队列中的任务,直到清空微队列。完成微队列的处理后,事件循环才会再次查看宏队列,执行下一个宏任务。这种机制确保了Promise的回调能够尽快得到执行,而不会被宏任务中的耗时操作阻塞。为了区分这两种队列,开发者通常需要对JavaScript的事件循环和任务执行顺序有深入的理解,从而写出性能更优、响应更快的代码。
宏队列(Macro Task Queue)和微队列(Micro Task Queue)是JavaScript中用于管理异步任务的两种不同的队列系统。它们之间的区别是异步任务的执行时机和顺序的关键。
1. 宏队列(Macro Task Queue):
- 宏任务通常包括script(整体代码)、setTimeout、setInterval、I/O操作、UI渲染等。
- 每个宏任务执行完毕后,浏览器会查看并处理微队列中的任务,但不会递归执行宏任务。
- 宏任务的执行不是连续的,它们之间会穿插微任务的执行。
- 宏任务由宿主环境(如浏览器)提供,主要完成与页面相关的任务,如DOM渲染。
2. 微队列(Micro Task Queue):
- 微任务包括Promise的回调、process.nextTick、MutationObserver等。
- 当一个宏任务执行完毕后,事件循环会首先处理微队列中的所有任务,这些任务是立即执行的,直到微队列为空。
- 微任务的连续性确保了在下一个宏任务执行之前,所有的微任务能够被及时处理。
- 微任务是JavaScript引擎用于在当前任务执行完毕后执行额外任务的一种机制。
在实际开发中,区分这两者是非常重要的,尤其是在处理异步代码逻辑时。例如,在编写基于Promise的异步操作时,开发者需要确保相关的回调函数能够按照预期的顺序执行,而不会被其他宏任务打断。
为了更深入理解宏队列和微队列的工作原理,可以通过一些实际代码示例进行测试和分析:
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
// 期望的输出顺序是:
// script start
// script end
// promise1
// promise2
// setTimeout
在上述代码中,可以观察到Promise的回调(promise1和promise2)是在script end之后执行的,且promise2会紧随promise1执行。只有当微队列清空之后,才会执行setTimeout的回调。
(图片来自:https://www.cnblogs.com/le-cheng/p/17477442.html)
JS 异步之宏队列与微队列
1 原理图
2 说明
- JS 中用来存储待执行回调函数的队列包含 2 个不同特定的队列:宏队列和微队列。
- 宏队列:用来保存待执行的宏任务(回调),比如:定时器回调 / DOM事件回调 / ajax 回调。
- 微队列:用来保存待执行的微任务(回调),比如:promise的回调 / MutationObserver的回调。
- JS 执行时会区别这 2 个队列:
- JS 引擎首先必须先执行所有的初始化同步任务代码。
- 每次准备取出第一个宏任务执行前,都要将所有的微任务一个一个取出来执行。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>10_宏队列与微队列</title>
</head>
<body>
<script>
setTimeout(() => { //会立即放入宏队列
console.log('timeout callback1()'); //后1
Promise.resolve(3).then(
value => { //会立即放入微队列
//每次取出宏任务前需要把所有的微任务处理掉
console.log('Promise onResolved3()', value); //timeout callback2()之前
}
)
}, 0);
setTimeout(() => { //会立即放入宏队列
console.log('timeout callback2()'); //后2
}, 0);
Promise.resolve(1).then(
value => { //会立即放入微队列
console.log('Promise onResolved1()', value); //先1
}
)
Promise.resolve(1).then(
value => { //会立即放入微队列
console.log('Promise onResolved2()', value); //先2
}
)
</script>
</body>
</html>
3 相关面试题
3.1 面试题1
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>11_Promise相关面试题1</title>
</head>
<body>
<script type="text/javascript">
setTimeout(() => { //放入宏队列 4
console.log(1);
}, 0)
Promise.resolve().then(() => { //放入微队列 2
console.log(2);
})
Promise.resolve().then(() => { //放入微队列 3
console.log(4);
})
console.log(3); //同步代码 1
//输出:3 2 4 1
</script>
</body>
</html>
3.2 面试题2
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>11_Promise相关面试题2</title>
</head>
<body>
<script type="text/javascript">
setTimeout(() => { //放入宏队列
console.log(1);
}, 0)
new Promise((resolve) => {
console.log(2); //同步执行
resolve();
}).then(() => { //放入微队列
console.log(3);
}).then(() => { //上面是pending状态,将此回调函数先存在callbacks中,3执行后放4
console.log(4);
})
console.log(5); //同步执行
//输出:2 5 3 4 1
/**
* 宏: [1]
* []
* 微: [3]
* [4]
* []
*/
</script>
</body>
</html>
3.3 面试题3
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>11_Promise相关面试题3</title>
</head>
<body>
<script type="text/javascript">
const first = () => (new Promise((resolve, reject) => {
console.log(3); //同步
let p = new Promise((resolve, reject) => {
console.log(7); //同步
setTimeout(() => { //放入宏队列
console.log(5);
resolve(6);
}, 0)
resolve(1); //p成功
})
resolve(2); //first()成功
p.then((arg) => { //放入微队列
console.log(arg); //1
})
}))
first().then((arg) => { //放入微队列
console.log(arg); //2
})
console.log(4); //同步
//3 7 4 1 2 5
/**
* 宏: [5]
* 微: [1, 2]
*/
</script>
</body>
</html>
3.4 面试题4
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>11_Promise相关面试题4</title>
</head>
<body>
<script type="text/javascript">
setTimeout(() => { //放入宏队列
console.log("0");
}, 0)
new Promise((resolve, reject) => {
console.log("1"); //立刻执行
resolve();
}).then(() => { //放入微队列 return undefined
console.log("2");
new Promise((resolve, reject) => {
console.log("3"); //3
resolve();
}).then(() => { //放入微队列
console.log("4");
}).then(() => { //.then()执行,内部缓存回调函数,然后外层.then()执行完毕
console.log("5");
})
}).then(() => { //放入微队列
console.log("6");
})
new Promise((resolve, reject) => {
console.log("7");
resolve();
}).then(() => { //放入微队列
console.log("8");
})
//1 7 2 3 8 4 6 5 0
/**
* 宏: [0]
* []
* 微: [2对应的回调函数, 8]
* [8, 4, 6]
* [4, 6]
* [6, 5]
* [5]
* []
*/
</script>
</body>
</html>