当我们执行js代码的时候,其实就是将我们的代码放到执行栈中执行,但是遇到异步代码的时候就会以另外一种方式进行。
JavaScript在代码运行到异步代码的时候会根据不同的异步,将代码放到对应的Task里面去
JavaScript一旦执行完执行栈里面的代码之后,Event Loop就会从task队列中拿出需要执行的代码并放入到执行栈中执行代码。
其中Task任务分为二种 microtask(微任务),macrotask(宏任务)
在 ES6 规范中,microtask 称为 jobs,macrotask 称为 task
- microtask(微任务)process.nextTick、Promise、MutationObserver 等
- macrotask(宏任务)setTimeout、setInterval、 setImmediate、script(整体代码)、 I/O 操作等。
Event Loop 过程解析
<script>
function func(num) { return function () { console.log(num) };}setTimeout(func(1));async function async3() { await async4(); console.log(8)}async function async4() { console.log(5)}async3();function func2() { console.log(2); async function async1() { await async2(); console.log(9) } async function async2() { console.log(5) } async1(); setTimeout(func(4))}setTimeout(func2);setTimeout(func(3));new Promise(resolve => { console.log('Promise'); resolve()}) .then(() => console.log(6)) .then(() => console.log(7));console.log(0); </script>
宏任务是一个一个的执行代码的;
微任务是将所有的任务执行完成之后在执行后面的代码的,
哪怕是在微任务阶段添加的微任务也会在当前阶段执行。
- 首先第一步将script标签放到宏任务队列中,之后将script标签放入执行栈中
| 宏任务macrotask | 微任务microtask | 执行栈 |
| script标签 | script标签 |
- 执行script标签里面的代码,逐行解析代码。并且将异步代码放到对应的Task中
| 宏任务macrotask | 微任务microtask | 执行栈 |
| 第一个 setTimeout | .then(6) | async4() |
| 第二个 setTimeout | new Promise | |
| 第三个 setTimeout | console.log(0) |
- 首先第一步遇到第一个 setTimeout,将定时器放到宏任务中;
- 第二步遇到async3() 。执行async3代码,然后调用async4(),运行里面的代码输出5,因为async3使用了async await 所以输出完成5之后await async4()会返回一个Promise,并且后面的代码会在Promise中执行。
async
function
async3
(
)
{
await
async4
(
)
;
console.
log
(
8
)
}
async
function
async4
(
)
{
console.
log
(
5
)
}
async3
(
)
;
// 转换
new
Promise
(
resolve [backcolor=rgba(255, 255, 255, 0.498039)]=>
{
console.
log
(
5
)
resolve
(
)
}
)
.
then
(
(
)
[backcolor=rgba(255, 255, 255, 0.498039)]=>
{
console.
log
(
8
)
}
)
// 上述代码转换之后就是这样 , 并且会在当前微任务结束之后在放入微任务队列
// new Promise里面的代码是同步执行的,如果遇到异步在调用Event Loop
- 下一步开始执行微任务,首先执行async3()里面返回的Promise.then()打印8。然后执行下一个微任务6
[td]输出6之后返回一个新的Promise对象,这时候微任务里面又多了一个任务,这个时候会先执行微任务,不会执行宏任务。要等到所有的微任务全部执行完成之后才会执行宏任务。
| 宏任务macrotask | 微任务microtask | 执行栈 |
| 第一个 setTimeout | .then(7) | console.log(6) |
| 第二个 setTimeout | ||
| 第三个 setTimeout |
[td]微任务执行完成之后await放入微任务队列
| 宏任务macrotask | 微任务microtask | 执行栈 |
| 第一个 setTimeout | .then(8) | console.log(7) |
| 第二个 setTimeout | ||
| 第三个 setTimeout |
| 宏任务macrotask | 微任务microtask | 执行栈 |
| 第一个 setTimeout | console.log(8) | |
| 第二个 setTimeout | ||
| 第三个 setTimeout |
- 下一步微任务已经都执行完成了,所以开始执行宏任务,并且在调用async1()方法的时候又创建了一个宏任务和一个await
| 宏任务macrotask | 微任务microtask | 执行栈 |
| 打印4的setTimeout | func(1) | |
| func2() | ||
| async1() | ||
| func(3) |
[td]微任务执行完成之后放入await;
| 宏任务macrotask | 微任务microtask | 执行栈 |
| 打印4的setTimeout | await async2()的的Promise.then() |
执行宏任务阶段,会将宏任务里面的代码推到执行栈里面执行
- 执行完成之后开始执行微任务
| 宏任务macrotask | 微任务microtask | 执行栈 |
| 打印4的setTimeout | console.log(9) |
- 微任务执行完成之后执行宏任务
| 宏任务macrotask | 微任务microtask | 执行栈 |
| console.log(4) |