前置知识
JS 是单线程,意味着执行前面代码,后面任务处于等待状态,不同于java多线程可以一次性执行多个事件。这如果遇到执行任务时间长的任务,这样会造成页面渲染不连贯,甚至加载阻塞。
这时候JS引入异步和同步任务两种,所谓“异步”,就是一个任务不连续完成,先完成一部分,回头“回调”执行剩下第二部分,甚至还有第三部分;同步则是主线程的任务,即按顺序执行机制连贯完成。
像文件读取,网络请求等任务属于异步任务:花费时间长,但是中间操作不用JS引擎完成,只需要执行来自别人准备好的数据就行,也就是回调过程。
这种JS“忙等”,常常采用“异步任务回调”模式:
这种方式也叫“非阻塞式”:等待异步任务的同时,JS引擎执行其他同步任务,等到执行栈里面没有任务,再不断看看任务队列里面是否有任务,执行回调。这种模式很显然,完成相同任务的同时,节约时间。
上面已经谈到事件循环(Event loop),这里定义一下。
事件循环,是有队列组成,异步回调遵从先进先出,在JS引擎空闲时候不断去事件队列里面找是否有完成了的异步任务,这也被称为循环。而事件任务分为,宏任务和微任务。下面根据图片看看
事件循环(Event Loop)
事件循环是JavaScript执行机制的核心,它确保宏任务和微任务按顺序执行。事件循环的基本步骤如下:
- 执行栈:JavaScript代码开始执行,首先执行同步代码。
- 任务队列:异步任务(如
setTimeout
)会被放入任务队列。 - 微任务队列:微任务会被放入微任务队列。
- 事件循环:当执行栈为空时,事件循环会检查任务队列和微任务队列,依次执行宏任务和微任务
宏任务和微任务
宏任务(Macro-tasks)
宏任务是JavaScript执行栈中的主要任务,包括代码执行、setTimeout
、setInterval
、I/O
操作等。宏任务执行完毕后,会检查是否有微任务需要执行,然后进入下一个宏任务。
微任务(Micro-tasks)
微任务是异步任务的一种,它们会在当前宏任务执行完毕后立即执行。微任务包括Promise
的then
回调、MutationObserver
等。
- Es6 引入promise 对象。
- js引擎发起异步任务,分为宏任务,微任务
- 一开始script里面就是宏任务,先执行微任务,再是宏任务
易错点
- 全局script 就是宏任务,如果有多个script,被浏览器解析成宏任务
<script>
console.log(0);
setTimeout(() => {
console.log(9);
},0);
</script>
<script>
console.log(3);
</script>
// 0 3 9
- await右边的表达式还是会立即执行,表达式之后的代码才是微任务, await微任务可以转换成等价的promise微任务分析
<script>
async function fn1() {
await fn2(); // await后面代码才是微任务
console.log(1);
}
async function fn2() {
console.log(2);
}
fn1();
console.log(3);
// 2 3 1
</script>
- promise 本身是同步的,后面调用的 .then() 才是微任务
<script>
const p = new Promise(resolve => {
console.log(9);
resolve(8);
})
p.then(res => console.log(res) );
console.log(1);
// 9 1 8
</script>
下面使用代码进行讲解:
console.log(1)
setTimeout(() => {
console.log(2)
}, 0)
const p = new Promise((resolve, reject) => {
console.log(3)
resolve(4)
})
p.then(result => console.log(result))
console.log(5)
思考3s
讲解:1. 宏任务队列中的代码首先执行,打印1
。
setTimeout
被添加到宏任务队列中,但不会立即执行。Promise
的构造函数立即执行,打印3
。Promise
的then
方法被添加到微任务队列中。console.log(5)
在宏任务队列中执行,打印5
。- 微任务队列中的
Promise
的then
方法执行,打印4
。 - 宏任务队列中的
setTimeout
执行,打印2
打印:1 3 5 4
邀你来挑战
试试成果吧,下面代码执行结果是什么?
<script>
console.log(2);
setTimeout(() => {
console.log(1);
const p = new Promise(resolve => {
console.log(9);
resolve(8);
})
p.then(res => console.log(res) )
}, 0);
const p = new Promise((resolve,reject) => {
setTimeout(() => {
console.log(3);
}, 0);
resolve(4);//打印成功 4
})
const p2 = new Promise( resolve => resolve(5) )
p2.then(res => console.log(res))
p.then (res => console.log(res))
console.log(7);
</script>