在理解事件循环机制之前,先说一下几点
队列【Queue】有出口和入口,先进先出
栈【Stack】先进后出,可以想象为一个箱子,先放进去的是在箱底,所以要出来,当然是后进去的先出去
堆【Heap】是应用程序在运行的时候请求操作系统分配给自己内存
堆是在程序运行时,申请相应大小的内存空间,即动态分配内存。堆是指程序运行是申请的动态内存,而栈是一种使用堆的方法。
栈是先进后出的,堆没有这个特性,两者都是存放临时数据的地方。 对于堆,我们可以随意的进行增加变量和删除变量,不要遵循什么次序。
JavaScript是一个单线程的脚本语言,这个原因很明显,浏览器解析Dom是顺序来的,假如它是多线程的,两个线程同时操作一个Dom,那浏览器将不知所措。
现代 JavaScript 引擎实现的一个图形化描述
一个 JavaScript 运行时包含了一个待处理的消息队列。每一个消息都关联着一个用以处理这个消息的函数。
分割线====================================================================
JavaScript宏任务(MacroTask)和微任务(MicroTask)
宏任务包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。
微任务包括: Promises, Object.observe, MutationObserver
执行流程
先执行同步代码,遇到异步宏任务则将异步宏任务放入宏任务队列中,遇到异步微任务则将异步微任务放入微任务队列中,当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行,微任务执行完毕后再将异步宏任务从队列中调入主线程执行,一直循环直至所有任务执行完毕。
考虑下面一段代码
console.log(1)
setTimeout(()=>{
console.log(2)
}, 1000)
new Promise((resolve)=>{
resolve()
console.log(3)
}).then(()=>{
console.log(4)
})
console.log(5)
输出: 1 3 5 4 2
过程解析
1,主程序进入,碰到顺序执行任务,控制台打印出1
2,扫描到异步的宏任务,放入宏任务队列
3,扫描到异步宏任务Promise, 控制台输出3,下一步回调异步微任务,放入微任务队列
4,扫描到同步任务,控制台输出5
5,主程序同步任务执行完毕, 查看异步微任务队列是否为空,非空则弹出执行相应的任务到主程序,控制台打印输出4,循环执行直到队列为空,
6, 主程序扫描宏任务队列, 查看是否为空,不为空的话弹出任务到主程序,控制台打印输出2,循环执行直到队列为空
7,主程序执行完毕