1.JS是单线程的
JS中的代码都是串行的, 前面没有执行完毕后面不能执行
2.执行顺序
-
程序运行会
从上至下依次执行所有的同步代码 -
在执行的过程中如果遇到异步代码会将异步代码放到事件循环中
-
当所有同步代码都执行完毕后, JS会不断检测 事件循环中的异步代码是否满足条件
-
一旦满足条件就执行满足条件的异步代码
3.宏任务和微任务
在JS的异步代码中又区分"宏任务(MacroTask)"和"微任务(MicroTask)"
宏任务: 宏/大的意思, 可以理解为比较费时比较慢的任务
微任务: 微/小的意思, 可以理解为相对没那么费时没那么慢的任务
4.常见的宏任务和微任务
宏任务: setTimeout, setInterval
微任务: Promise, MutationObserver,process.nextTick(node独有)
注意点
所有的宏任务和微任务都会放到自己的执行队列中, 也就是有一个宏任务队列和一个微任务队列
所有放到队列中的任务都采用"先进先出原则"(先放进去的任务会先执行,后放进去的任务会后执行), 也就是多个任务同时满足条件, 那么会先执行先放进去的
5.演示
上面的基本宏任务和微任务都了解过,process.nextTick这个留着下篇文章再讲
本节先来看看这个MutationObserver
MutationObserver是专门用于监听节点的变化
下面简单来写个例子
<div></div>
<button class="add">添加节点</button>
<button class="del">删除节点</button>
<script>
// 1.获取元素
let oDiv = document.querySelector('div');
let oAddBtn = document.querySelector('.add');
let oDelBtn = document.querySelector('.del');
// 2.监听点击事件
oAddBtn.onclick = () => {
let op = document.createElement("p");
op.innerText = "我是段落";
oDiv.appendChild(op);
}
oDelBtn.onclick = () => {
let op = document.querySelector("p");
oDiv.removeChild(op);
}
// 3.创建MutationObserver任务
let mb = new MutationObserver(() => {
console.log('节点发生了变化');
})
// 4.监听对象(下面代码含义: 监听oDiv元素子节点的变化)
mb.observe(oDiv, {
"childList": true
})
// 5.如何验证MutationObserver是微任务(异步)
// 下面再加点同步代码
// 如果同步代码先执行,是不是就表示确实是异步代码
console.log("同步代码Start");
console.log("同步代码End");
</script>
6.完整执行顺序
-
从上至下执行所有同步代码
-
在执行过程中遇到宏任务就放到宏任务队列中,遇到微任务就放到微任务队列中
-
当所有同步代码执行完毕之后, 就执行微任务队列中满足需求所有回调
-
当微任务队列所有满足需求回调执行完毕之后, 就执行宏任务队列中满足需求所有回调
注意点
每执行完一个宏任务都会立刻检查微任务队列有没有被清空, 如果有就满足的微任务就立即执行微任务
那下面来看两个例子应该就懂了
例子一
setTimeout(function () {
console.log("setTimeout1");
}, 0);
Promise.resolve().then(function () {
console.log("Promise1");
});
console.log("同步代码Start");
Promise.resolve().then(function () {
console.log("Promise2");
});
setTimeout(function () {
console.log("setTimeout2");
}, 0);
console.log("同步代码End");
我们来简单分析,上面是不是说过,有同步代码先执行同步代码,异步代码遇到自己对应的任务队列就按先后顺序排放
所以先输出的肯定是同步代码start和同步代码end
之后是不是先执行微任务的代码,按照先进先出的原则,是不是p1先进来,p2后进来,p1和p2是不是都满足条件 可以立即执行
所以静跟着输出的结果是Promise1和Promise2
微任务执行完了是不是就去执行宏任务
宏任务中是不是s1先进来,s2后进来,是不是延迟0秒就执行,是不是都满足条件
所以最后输出的就是setTimeout1和setTimeout2
怎么验证我的说法?
看看浏览器打印出来的结果
是不是跟我说的一模一样
看完了例子一,来看例子二
例子二
setTimeout(function () {
console.log("setTimeout1");
Promise.resolve().then(function () {
console.log("Promise1");
});
Promise.resolve().then(function () {
console.log("Promise2");
});
}, 0);
setTimeout(function () {
console.log("setTimeout2");
Promise.resolve().then(function () {
console.log("Promise3");
});
Promise.resolve().then(function () {
console.log("Promise4");
});
}, 0);
我还是简单来说一下
从上至下进行执行,首先先遇到一个setTimeout,这个是不是一个宏任务,然后就这个放入宏任务的队列
之后遇到一个setTimeout,这个是不是也是一个宏任务,然后又把这个放入宏任务的队列里
(有人肯定说,诶你Promise为什么不放入宏任务,因为promise是setTimeout里面的东西,我们执行代码是不是先执行外面的,再执行里面的,又因为setTimeout是不是一个异步代码,就不会立即执行,所以promise还没有执行就不存在放入微任务里面,这应该属于基本常识,我就不做过多解释了)
这样我们的代码是不是就执行完毕了,执行完毕我们来看一下
假如前面的setTimeout叫做s1,后面的叫做s2,,因为内存中没有微任务,是不是就直接执行宏任务了
是不是s1先进入,s2后进入,采取先进先出的原则,就先执行s1,先执行s1,遇到一个打印setTimeout1,
这是不是一个同步代码,就立即执行了,然后遇到一个Promise1,这是不是一个微任务,就把p1放入微任务队列里然后又执行遇到一个promise2,也是一个微任务,然后放入微任务里
这样s1的代码是不是就执行完毕了,输出结果是不是setTimeout1
上面是不是说过每执行完一个宏任务,就会去看微任务里有没有可以执行的微任务,我们执行完s1,是不是微任务里面有两个promise,是不是都满足条件可以执行
所以紧接着我们就会把s2先放一下,执行微任务,是不是p1先进入,p2后进入,采用先进先出的原则,先执行p1 ,再执行p2,紧接着就输出了promise1和promise2
这样微任务是不是都执行完了,微任务执行完了就会去执行宏任务,宏任务还有一个s2,s2是不是延迟0s就表示立即执行,是不是满足条件可以执行
执行遇到一个同步代码就直接输出setTimeout2,之后遇到一个p3微任务,p4微任务,是不是和前面一样,放入微任务队列,s2就执行完毕了
s2执行完毕会看微任务有没有可以执行的任务,p3和p4是不是都满足,所以按着先后顺序,是不是就打印promise3和promise4
怎么验证我的说法
看看浏览器打印的结果
是不是没有任何的问题了
听懂了我还留了一个小练习
7.小练习
setTimeout(function () {
console.log("setTimeout1");
Promise.resolve().then(function () {
console.log("Promise2");
});
Promise.resolve().then(function () {
console.log("Promise3");
});
}, 0);
Promise.resolve().then(function () {
console.log("Promise1");
setTimeout(function () {
console.log("setTimeout2");
});
setTimeout(function () {
console.log("setTimeout3");
});
});
请把输出结果答在评论区(qvq)