【JS档案揭秘】第二集 Event loop与执行栈

时间:2020-12-03 15:19:05

  我时常在思考关于JS的很多知识在工作中有什么用?是否只能存在于面试这种理论性的东西中,对于我们的业务和工作,它们又能扮演怎样的角色。以后在JS档案揭秘的每一期里,都会加入我对于业务的思考,让这些知识不再是空中楼阁,而是有实际操作的意义。

业务场景

  所有的核心在于执行顺序,它能帮助我们正确判断代码按照怎样的顺序去执行。避免因为执行顺序与预期不一致而导致的bug。如果遇到这种因为执行顺序问题而产生的bug,也能通过event loop分析出正确的执行顺序,定位bug并解决。

关键词解释

  执行栈:一个专门用来存放执行代码的栈内存结构,它就像一个容器,也叫做执行的主线程。如果遇到函数,会根据回调关系把回调层级深的函数先推入到执行栈底部执行,其他语句依次推入,当全部执行完了后,会从上到下依次弹出执行代码(也就是“先进后出”),供下一次使用;

  宏任务(只针对浏览器环境):宿主环境提供的任务,包括:script代码块,MessageChannel,requestAnimationFrame,setTimeout,setInterval;

  微任务(只针对浏览器环境):JS自带的任务,包括promise的then和catch,MutationObserver;

  任务队列:由宏任务队列与微任务队列组成;

  event loop:又名事件循环,它会不断地去微任务队列里取任务放到执行栈执行,当微任务队列被清空时,才去宏任务队列取任务执行。假如这个宏任务里面碰到了微任务和宏任务,会分别推入到任务队列中,并按照“只有微任务队列清空才执行宏任务”的原则来执行代码;

  

画图理解

  【JS档案揭秘】第二集 Event loop与执行栈

过程叙述  

  当我们说“浏览器是 JS 的家”时,这句话真正的意思是浏览器提供运行时环境来执行我们的JS代码。

  浏览器的主要组件包括JS引擎,事件循环,任务队列和Web APIs。上图的任务队列有一点瑕疵,任务队列应该有两个(宏任务队列与微任务队列)。

  JS引擎从堆中取出JS代码并及进行分析。假设是一段或多段script代码,它会被认为是一个或几个宏任务,并推入到web apis中。web apis看到是script代码,就把它推入到宏任务队列中,event loop从宏任务队列取到这一段JS代码,并放入执行栈中执行。

  在执行当前宏任务时,每当它遇到一些异步代码,如setTimeout,它又会把它推入到web apis中去执行。当异步代码在Web APIs被执行完后,其回调callback 就被送往任务队列。

  event loop不断地监视任务队列(Task Queue),并按它们排队的顺序一次处理一个回调。每当调用栈为空,也就是同步代码执行完毕时,event loop会不断地去微任务队列里取任务放到执行栈执行,当微任务队列被清空时,才去宏任务队列取任务执行。请记住,如果调用栈不是空的,则事件循环不会将任何回调推入执行栈

例题分析

  【JS档案揭秘】第二集 Event loop与执行栈

  按照我们之前的理论,首先这里分成两个宏任务,script1和script2。

  script1的同步代码先执行,打印111,333。微任务队列推入222,宏任务队列推入444。

  此时微任务队列只有222,而宏任务队列为“script2 --- 444”。

  所以先清空微任务队列,打印出222,再从宏任务队列中取出script2,推到执行栈中执行。

  script2的同步代码先执行,打印555,777。并将666推入微任务队列,888推入宏任务队列。

  此时微任务队列只有666,而宏任务队列为“444 --- 888”。

  所以先清空微任务队列,打印出666,再从宏任务队列依次取出444和888,并推到执行栈中执行。

  综上,打印结果如下:

  【JS档案揭秘】第二集 Event loop与执行栈