这是我参与「第四届青训营 」笔记创作活动的第5天
前言
js 是单线程执行的,js中的任务按顺序一个一个的执行,但是一个任务耗时太长; 那么后面的任务就需要等待,为了解决这种情况,将任务分为了同步任务和异步任务; 而异步任务又可以分为微任务和宏任务。
从上面我们可以看到,同步任务进入主线程,即主执行栈,异步任务进入任务队列,主线程内的任务执行完毕为空,会去任务队列读取对应的任务,推入主线程执行。上述过程的不断重复就事件循环
事件循环
事件队列
js引擎遇到一个异步任务后并不会一直等待其返回结果,而是会将这个任务交给浏览器的其他模块进行处理(以webkit为例,是webcore模块),继续执行调用栈中的其他任务。当一个异步任务返回结果后,js引擎会将这个任务加入与当前调用栈不同的另一个队列,我们称之为事件队列。
当一个脚本执行的时候,js引擎会解析这段代码,并将其中的同步代码按照执行顺序加入调用栈中,然后从头开始执行。
js引擎遇到一个异步事件后并不会一直等待其返回结果,而是会将这个事件挂起(其他模块进行处理),继续执行执行栈中的其他任务。当一个异步事件返回结果后,js会将这个事件加入到事件队列。
被放入事件队列不会立刻执行其回调,而是等待当前执行栈中的所有任务都执行完毕, 主线程处于闲置状态时,主线程会去查找事件队列是否有任务。如果有,那么主线程会从中取出排在第一位的事件,并把这个事件对应的回调放入执行栈中,然后执行其中的同步代码…,如此反复,这样就形成了一个无限的循环。这个过程被称为事件循环(Event Loop)。
宏任务与微任务
在js中,又将异步任务分为宏任务和微任务,所以,上述任务队列也分为宏任务队列和微任务队列,那么,什么是宏任务,什么是微任务呢?
I/O、定时器、事件绑定、ajax等都是宏任务
Promise的then、catch、finally和process的nextTick都是微任务
注意:Promise的then等方法是微任务,而Promise中的代码是同步任务,并且,nextTick的执行顺序优先于Promise的then等方法,因为nextTick是直接告诉浏览器说要尽快执行,而不是放入队列
js中,微任务总是先于宏任务执行,也就是说,这三种任务的执行顺序是:同步任务>微任务>宏任务
| 任务(代码) | 宏/微 任务 | 环境 |
|---|---|---|
| script | 宏任务 | 浏览器 |
| 事件 | 宏任务 | 浏览器 |
| 网络请求(Ajax) | 宏任务 | 浏览器 |
| setTimeout() 定时器 | 宏任务 | 浏览器 / Node |
| fs.readFile() 读取文件 | 宏任务 | Node |
| Promise.then() | 微任务 | 浏览器 / Node |
先执行同步代码
● 遇到宏任务,放入队列
● 遇到微任务,放入微任务队列
● 执行栈为空
● 将微任务入栈执行
● 所有的微任务完成之后,取出宏任务队列来执行
同步代码 => 微任务 => 宏任务
总结
例如, 有代码如下
console.log(1);
async function fun1(){
console.log(6);
await fun2();
console.log(7);
}
setTimeout(()=>{
console.log(9);
},0)
async function fun2(){
new Promise((resolve)=>{
console.log(2);
resolve(3);
setTimeout(()=>{
console.log(4);
},0)
}).then((data)=>{
console.log(data)
}).then(()=>{
console.log(5);
})
}
fun1()
console.log(8);
结果为:1,6,2,8,3,7,5,9,4