摘要:而事件循环机制主要以来调用栈来处理执行顺序,依靠任务队列来执行代码的执行。当微任务执行完毕之后,第一轮循环结束,进入第二轮循环,继续执行宏任务,此时执行,进入函数调用栈,输出。
事件循环机制控制了javascript代码的执行顺序。我们都知道javascript是单线程,这个线程中拥有唯一的一个事件循环。(新标准web workker有多线程的概念。)而事件循环机制主要以来调用栈来处理执行顺序,依靠任务队列来执行代码的执行。队列的概念可以参考https://segmentfault.com/a/11...
在一个线程中,调用栈是唯一的,但是任务队列可以是多个,并且分为macro-task(宏任务)及micro-task(微任务)两种类型。
这里需要区分一个概念任务及任务源。setTimeout及Promise是任务源。他们指定具体的执行任务进入任务队列。只有回调中的函数才会进入任务队列。就像setTimeout它其实是丽姬执行的,只是它的回调函数才会延迟执行。promise也是,本身是立即执行的,但是then才会在“未来”执行。
javascript的执行顺序是从整体代码开始做循环,之后全局上下文进入函数调用栈。直到调用栈清空。整体代码所处的macro-task执行完成,轮到micro-task任务执行。一直循环直到所有的任务执行完成。
当然,不同的任务源的任务会进入不同的任务队列。
具体的可以参考一下代码。
1.事件循环从macro-task开始,整体代码开始执行。整体代码script进入macro-task,并且执行代码main进入调用函数调用栈。遇到第12行的打印输出start
2.继续执行,遇到13行的setTimeout,它是宏任务源。便将其分发到对应的队列中。接着遇到16行的promise。promise.resolve会进入函数调用栈直接执行,因此打印promise1,接着将p.then1和p.then分发到对应的微任务队列中。继续执行代码,遇到第24行的打印便输出end。大致图示如下图。
3.script执行完毕,即第一个宏任务执行完毕,开始执行微任务。现在微任务只有一个队列,里面有p1.then1,p1.then2。队列是先进先出,因此先执行p1.then1,p1.then1进入函数调用栈,输出then1。
4. p1.then1执行完毕之后,出栈。但是此时的正在进行的微任务还未执行完完毕,会继续执行p1.then2,p1.then2进入函数调用栈,输出then2。此时,微任务正在进行的队列已经执行完毕。
5.当微任务执行完毕之后,第一轮循环结束,进入第二轮循环,继续执行宏任务,此时setTimeout执行,进入函数调用栈,输出setTimeout1。
6.此时,宏任务队列和微任务队列中都没有任务了。代码执行完毕,就不会有任何输出了。
我们上述的代码只涉及到一个宏任务及微任务队列的情况。但如果情况更加复杂会有什么样的表现呢?大家可以看看下面的代码。根据上面的原理试着自己分析下结果~
1.还是跟以前的例子一样,事件循环从macro-task开始,整体代码开始执行。输出start。setTimeout1,setTimeout2依次进入新的宏任务队列。p3.resolve执行,输出promise31,promise31。并将setTimeout3放入新的宏任务队列。因为setTimeout3不是整体代码中定义的,而是在promise中定义的,需要重新开启一个宏任务队列。然后p3.then1,p3.then2分别进入微任务队列。p3.resolve出栈后,整体代码继续执行,这里就不重新画图了,输出end。
2.整体代码已执行完成,循环进入微任务。此时p3.then1进入函数调用栈。输出then31。遇到新的定时,将set4放入宏任务队列。遇到新的promise,继续将p4.resolve入栈。输出promise41,promise42。遇到新的定时,将set5放入宏任务队列。此时需要注意的是,在微任务中继续有promise。此时的promise.then不再进入微任务队列,而是直接执行。因此输出then41。
3.微任务队列还未执行完毕,继续执行p3.then2。直接输出then32。此时微任务队列已经执行完毕,进入下一轮循环。
4.新的循环开始。队列是先进先出,因此在宏任务当前队列中,set1先执行,进入函数调用栈。输出setTimeout1。遇到新的promise,继续将p1.resolve入栈。输出promise1。还是跟上看一样,在宏任务中继续有promise。此时的promise.then不再进入微任务队列,而是直接执行。直接输出then1。
5.setTimeout1执行完毕,正在执行的宏任务队列还有任务,继续执行setTimeout2。setTimeout2进入函数调用栈。跟setTimeout1的分析一样,陆续输出setTimeout2,promise2,then2。
6.当前宏任务执行完毕,微任务内没有可执行的队列。继续下一轮循环。执行set3。输出setTimeout3。遇到新的promsie,还是跟上面的分析一样,输出promise5,then5。因为微任务一直没有可执行的队列。宏任务内的队列依次执行,输出setTimeout4,setTimeout5。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/104733.html
摘要:主线程不断重复上面的三步,此过程也就是常说的事件循环。所以主线程代码执行时间过长,会阻塞事件循环的执行。参考资料这一次,彻底弄懂执行机制任务队列的顺序机制事件循环搞懂异步事件轮询与中的事件循环 1. 说明 读过本文章后,您能知道: JavaScript代码在浏览器中的执行机制和事件循环 面试中经常遇到的代码输出顺序问题 首先通过一段代码来验证你是否了解代码输出顺序,如果你不知道输出...
摘要:了解事件循环机制有助于理解的执行过程,同时这也是面试常见题。那么这个回调函数将在何时由谁执行呢已知是浏览器环境提供的,因此浏览器将对它进行处理,浏览器会在本次事件完成,即计时结束后,将回调函数加入循环队列中,然后等待被加入执行栈执行。 如果有人问JavaScript是什么,也许你会说它是一个单线程、非阻塞、异步、解释型的脚本语言。那么作为一个单线程语言,它是怎么实现非阻塞、异步的?这就...
摘要:如果没有其他异步任务要处理比如到期的定时器,会一直停留在这个阶段,等待请求返回结果。执行的执行事件关闭请求的,例如事件循环的每一次循环都需要依次经过上述的阶段。因此,才会早于执行。 showImg(https://segmentfault.com/img/bVbnY76); 概念 同步任务(Synchronous) 在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务 ...
摘要:主线程要明确的一点是,主线程跟执行栈是不同概念,主线程规定现在执行执行栈中的哪个事件。主线程循环即主线程会不停的从执行栈中读取事件,会执行完所有栈中的同步代码。以上参考资料详解中的事件循环机制中的事件循环运行机制详解再谈 showImg(https://segmentfault.com/img/remote/1460000015317437?w=1920&h=1080); 前言 大家都...
摘要:前端基础进阶正是围绕这条线索慢慢展开,而事件循环机制,则是这条线索的最关键的知识点。特别是中正式加入了对象之后,对于新标准中事件循环机制的理解就变得更加重要。之后全局上下文进入函数调用栈。 showImg(https://segmentfault.com/img/remote/1460000008811705); JavaScript的学习零散而庞杂,因此很多时候我们学到了一些东西,但...
阅读 993·2023-04-25 19:35
阅读 2631·2021-11-22 09:34
阅读 3678·2021-10-09 09:44
阅读 1713·2021-09-22 15:25
阅读 2931·2019-08-29 14:00
阅读 3371·2019-08-29 11:01
阅读 2594·2019-08-26 13:26
阅读 1735·2019-08-23 18:08