JS事件循环,了解一下?
在理解事件循环之前,我总会遇到一些奇奇怪怪的问题:比如明明已经调接口拿到了数据,可是跟在调数据之后的操作却没有正常执行;又或者不知道为啥,代码里非得加个setTimeout
才能正常跑通;特别是在运用Promise的时候,更是有各种问题百思不得解。遇上问题要解决,更要知道问题产生的原因,这样才能hold住全场!
废话不多说了,先来看一段代码
console.log('start');
setTimeout(function(){
console.log('setTImeout1')
},0);
new Promise(function(resolve,reject){
console.log('resolve')
setTimeout(function(){
console.log('setTimeout2')
},200);
resolve()
}).then(function(){
console.log('then')
});
setTimeout(function(){
console.log('setTimeout3')
},0);
console.log('end');
结果是start resolve end then setTimeout1 setTimeout3 settimeout2
。
在分析结果之前,我先来科普几个概念,这些概念的表述不一定与标准完全对应,但是可以帮助你更容易理解JS的事件机制
-
宏任务(macro-task):包括js整体代码,setTimeout,setInterval,setImmediate ,I/O, UI renderder等
-
微任务(micro-task):包括Promise,Object.observe,process.nextTick,MutationObserver等
-
调用栈:js被加载进来之后,会从上至下读取代码,同步代码被立即执行,而异步代码被加入事件队列中
- 事件队列:一些没有被立即执行的代码被添加到事件队列中,队列是一种先进先出的数据结构,也就是说,先加入事件队列的任务会被优先执行
我们知道,js是单线程的,这就是说,只有一个主线程,主线程会自上而下依次执行调用栈中的事件。任务队列中的代码被加载到函数调用栈中去执行。当前的宏任务队列中的代码执行完毕后,会执行本次宏任务队列中分发到微任务队列中的代码。然后执行下一个宏任务队列中的代码,依次循环。
这里要提一点容易误解的地方,setTimeout
函数本身,其实是立即执行的,它内部的任务,才会被分发到任务队列中延时执行。
-
代码被加载后,全局上下文进入函数调用栈,紧接着,‘start’被执行
-
遇到
setTimeout
的时候,新建了一个宏任务队列,函数内的任务被分发这个队列中等待执行 -
此时遇到了
Promise
,注意,Promise
中的第一个function中的代码会立马开始执行,遇到resolve
或者reject
后,then
方法中函数会被分发到本次事件循环的微任务队列中等待执行。所以这里立马打印出了'resolve'。遇到setTimeout2
后,同样新建了一个宏任务队列,其中的函数被分发到了这个新的宏任务队列中,then
方法中的操作被分发到了微任务队列中等待 -
代码继续往下,遇到'setTimeout3'后再次新建了一个新的宏任务队列
-
'end'被立即执行。此时有三个宏任务队列,一个微任务队列
-
微任务队列中的操作被执行,也就是打印出了‘then’,此时,第一轮的事件循环结束。
- 第一轮的事件循环结束,开始下一轮的事件循环,依次执行每个宏任务队列中的内容,我们这里宏任务队列中的函数比较简单,都是
console
操作,所以并没有再分发新的任务队列,但是由于第二个setTimeout
设定了200毫秒的延时,所以‘setTimeout2’被最后打印。
说到这里,你基本上对事件循环有个大致的了解了。之前有个同学问过我一个问题,点击轮播图下一页,但是页面没有反应,代码是这样的:
这个函数是点击下一页的按钮后轮播图转动,他在getNextPhoto
函数中调接口获取了下个页面的数据,goToPage
函数里是让轮播图切换的操作。相信如果你读懂了这篇文章,就会知道问题出在了哪里。显然获取数据ajax
是个异步操作,他被分发到了事件队列中等待执行,所以还没等数据回来,翻页的操作已经开始执行,由于没有数据,并没有按预期效果显示。解决方法就是在getNextPhoto
函数里调接口拿到数据之后,再通知去执行goToPage
操作,问题就解决了。
作为一个前端菜鸟,希望得到各位大神的批评指正!
共同学习,写下你的评论
评论加载中...
作者其他优质文章