带你彻底了解JS浏览器事件循环(eventloop)
Tips: JS事件循环分为客户端事件循环(浏览器)、服务端事件循环(Node),此次讲的是浏览器事件循环,另外涉及到进程线程方面的知识不多讲,请自行参考了解:
参考文章:
http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html
https://www.cnblogs.com/yqx0605xi/p/9267827.html
一、什么是JS浏览器事件循环
JS有执行栈(后进先出)、任务队列(先进先出)、主线程(任务的执行过程),栈内的任务都执行完被清空了,意味着主线程是空闲状态,主线程空闲状态时会自动寻找任务队列读取任务放入执行栈中。每次栈内被清空,都会去读取任务队列有没有任务,有就读取执行,一直循环读取-执行的操作,就形成了事件循环。
二、任务的分类(JS单线程中的任务)
任务可以有两种分类方法,广义任务、狭义任务
1、广义任务可分为同步任务、异步任务
JS 的执行顺序就是每次事件循环中的同步任务-异步任务。
2、狭义任务可分为宏任务、微任务
JS 的执行顺序就是每次事件循环中的宏任务-微任务。
常见的宏任务:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering,requestAnimationFrame等
常见的微任务:process.nextTick()、promise.then()(new Promise不算!)、Object.observe(), MutationObserver,await(await后面的)等
三、夺命十题
1、
console.log(1); setTimeout(function() { console.log(2); }) var promise = new Promise(function(resolve, reject) { console.log(3); resolve(); }) promise.then(function() { console.log(4); }) console.log(5);
2、
async function async1(){ console.log('1') await async2() console.log('2') } async function async2(){ console.log('3') } console.log('4') setTimeout(function(){ console.log('5') },0) async1(); new Promise(function(resolve){ console.log('6') resolve(); }).then(function(){ console.log('7') }) console.log('8')
第1题答案及解析:
1,3,5,4,2
- 上面的示例中,第一次事件循环,整段代码作为宏任务进入主线程执行。
- 遇到了 setTimeout ,就会等到过了指定的时间后将回调函数放入到宏任务的任务队列中。
- 遇到 Promise,将 then 函数放入到微任务的任务队列中。
- 整个事件循环完成之后,会去检测微任务的任务队列中是否存在任务,存在就执行。
- 第一次的循环结果打印为: 1,3,5,4。
- 接着再到宏任务的任务队列中按顺序取出一个宏任务到栈中让主线程执行,那么在这次循环中的宏任务就是 setTimeout 注册的回调函数,执行完这个回调函数,发现在这次循环中并不存在微任务,就准备进行下一次事件循环。
- 检测到宏任务队列中已经没有了要执行的任务,那么就结束事件循环。
- 最终的结果就是 1,3,5,4,2。
第2题答案及解析:
4,1,3,6,8,2,7,5
- 上面的示例中,第一次事件循环,整段代码作为宏任务进入主线程执行。
- 先打印4,遇到了 setTimeout ,将其回调放到宏任务队列最后。
- 执行async1函数,打印1,遇到await,相当于.then,打印3,将await下面操作(打印2)放入微任务队列。
- 遇到new Promise,打印6,将其回调函数放入微任务队列,打印8。
- 第一次宏任务的循环结果打印为: 4,1,3,6,8,进而寻找微任务。
- 微任务为2和7,按顺序打印2,7。第一次事件循环结束,准备进行下一次事件循环。
- 检测到宏任务队列中存在5,打印5,寻找宏任务完毕,寻找微任务,没有找到微任务,准备进行下一次事件循环。
- 检测到宏任务队列中已经没有了要执行的任务,那么就结束事件循环。
- 最终的结果就是 4,1,3,6,8,2,7,5