事件循环(Event Loop)
堆、栈、队列
- 堆:是一种数据结构,是利用完全二叉树维护的一组数据。
- 栈:只能表尾进行插入或删除的操作的线性表,它按照后进先出的远侧存储数据。
- 队列:只允许在表的前端进行删除操作,而在表的后端进行插入操作的线性表。故队列又称为先进先出
JS调用栈
JavaScript是一门单线程语言,它有一个主线程和调用栈(执行栈),所有的任务都会被放到调用栈等待主线程执行。
既然是栈肯定是采用后进先出的规则,当函数执行的时候,会被添加到栈的顶部,当执行栈执行完成后,就会从栈顶移除,直到栈内被清空。


解释:js从上倒下解析方法,将其中的同步任务按照书讯排列到执行栈中,当程序调用外部的api时,将此类异步任务挂起,继续执行同步任务中的任务。等异步任务返回结果后,再按照顺序排列到任务队列中,主线程先将执行栈中的同步任务清空,然后检查任务队列中是否有任务,如果有,就将第一个时间对应的回调推到执行炸栈中执行,若在执行过程中再次遇到异步任务,则继续挂起,等一步任务返回结果后,再次按照顺序排列到任务队列中,如此反复,直至任务队列为空,这个循环的过程被称为ecent loop事件循环。
举个例子:
setTimeout(() => {
console.log('一个0秒的计时器')
}, 0)
new Promise(resolve => {
console.log('new Promise')
resolve('.then')
}).then(res => {
console.log(res)
})
console.log('开始执行')
/*
* new Peomise, 开始执行,.then ,一个0秒的计时器
*/
首先setTimeout会被挂起,new Promise正常执行,.then回调函数被挂起。console.log()正常执行。
那么为啥会是先输出.then,而不是setTimeout,接着往下看。
宏任务、微任务
除了广义的同步任务和异步任务,我们对任务有更精细的定义,一种宏任务(MacroTask)也叫做Task,一种叫做微任务(MicroTask)
- 宏任务:当前调用栈中执行的任务为宏任务。包含全部的script代码、
setTimeout、setInterval、setImmediate、I/O、UI Rendering - 微任务:当前(此次事件循环中)宏任务执行完成,在下一个宏任务开始之前需要执行的任务为微任务。包含Process.nextTick(Node独有)、Promise、Object.observe(废弃)、MutationObserver
- 不同类型的任务会进入对应的
event Queue,宏任务中的事件放在callback queue中,由事件触发线程维护;微任务的事件放在微任务队列中,由js引擎线程维护。
不同类型的任务会进入对应的Event Queue,比如setTimeout和setInterval会进入相同的Event Queue。
在ES3以及以前的版本中,JavaScript本身没有发起异步请求的能力,也就没有微任务的存在。在ES5之后,JavaScript引入了Promise,这样,不需要浏览器,JavaScript引擎自身也能够发起异步任务了。
两者区别为:
| 宏任务(macrotask) | 微任务(microtask) | |
|---|---|---|
| 谁发起的 | 宿主(Node、浏览器) | JS引擎 |
| 具体事件 | 1. script (可以理解为外层同步代码) 2. setTimeout/setInterval 3. UI rendering/UI事件 4. postMessage,MessageChannel 5. setImmediate,I/O(Node.js) | 1. Promise 2. MutaionObserver 3. Object.observe(已废弃;Proxy 对象替代) 4. process.nextTick(Node.js) |
| 谁先运行 | 后运行 | 先运行 |
| 会触发新一轮Tick吗 | 会 | 不会 |

浙公网安备 33010602011771号