JavaScript是一个单线程的脚本语言,所以在一行代码执行的过程中,必然不会存在同时执行的另一行代码。
如果有些代码执行了大量的计算,比方在前端破解密码之类的操作,就会导致后续代码一直在等待,页面处于假死状态,因为前边的代码没有执行完。
所以如果所有代码都是同步执行的,对于一些请求难道要一直循环代码去判断是否拿到了返回结果么,于是就有异步事件的概念。注册一个回调函数,比如说发一个网络请求,我们告诉主程序等到接收到数据后通知我,然后我们就可以去做其他事情了。
然后在异步完成后,会通知我们,但是此时可能程序正在做其他事情,所以即使异步完成了也要在一旁等待,等到程序空闲下来才有时间去看哪些异步已经完成了,可以去执行。
微任务和宏任务的区别
多个宏任务合在一起可以认为有一个任务队列在这,任务队列中的都是已经完成的异步操作,在当前的微任务没有执行完成时,是不会执行下一个宏任务的。
- 两者都是异步
- 进程的切换肯定是宏任务,因为需要花费大量的资源
- 线程的切换是微任务,只需要在一个进程中切换,更确切地说,微任务时纤程的切换导致的,纤程是比线程更小的概念
- 为什么跟定时器有关的任务是宏任务? 因为计时器是实时的,不能被阻塞,所以定时器被设计在另一个进程中被管理,所以定时器任务会有进程的切换。
- 事件为什么是宏任务? 事件的触发依赖于浏览器的实现,平台有其自己的事件注册和派发机制,事件的管理中心也一定是在另外一个进程中实现的。
宏任务
- script整体代码
- setTimeout
- setInterval
- requestAnimationFrame
- I/O
微任务
- Promise.then catch finally
- mutationObserver
什么是事件循环
JavaScipt代码执行的过程中,除了依靠函数调用栈来确定函数的执行顺序外,还依靠任务队列来确定代码执行,整个执行过程称为事件循环过程。一个线程中,事件循环是唯一的,但是任务队列可以拥有多个。任务队列又分为宏任务与微任务。
所有的任务可以分为同步任务和异步任务。同步任务就是立即执行的任务,而异步任务就是异步执行的任务。异步任务会通过任务队列的机制来进行协调。每个宏任务执行完,都会检查有没有微任务,如果有,执行完所有的微任务再执行下一个宏任务。
浏览器的事件循环
浏览器的事件循环由一个宏任务队列+多个微任务队列组成。
首先,执行第一个宏任务:全局script脚本,产生的宏任务和微任务进入各自的队列中。执行完script后,把当前的微任务队列清空,完成一次事件循环。
接着再取出一个宏任务,同样把在此期间产生的回调入队,再把当前的微任务队列清空。
宏任务队列只有一个,而每一个宏任务都有一个自己的微任务队列, 每轮循环都是由一个宏任务+多个微任务组成。
Promise.resolve().then(()=>{
console.log('第一个回调函数:微任务1')
setTimeout(()=>{
console.log('第三个回调函数:宏任务2')
},0)
})
setTimeout(()=>{
console.log('第二个回调函数:宏任务1')
Promise.resolve().then(()=>{
console.log('第四个回调函数:微任务2')
})
},0)
// 第一个回调函数:微任务1
// 第二个回调函数:宏任务1
// 第四个回调函数:微任务2
// 第三个回调函数:宏任务2