前端的经典的面试题——Event Loop(事件循环)


我们今天来说说javaScript中的代码执行顺序问题,这是一道非常经典的面试题。

这里我们需要知道的一个知识点是:javascript是一门单线程的脚本语言,代码的执行顺序是自上而下执行的,我们来看一下下面这段代码的执行结果:

console.log(1);

console.log(2);

console.log(3);

//执行结果:1;2;3;

这段代码是自上而下执行的。

我们再看下面这段代码的执行结果:

console.log(1);

setTimeout(function () {

console.log(2);

}, 0);

console.log(3);

// 执行结果:1;3;2;

 

这段代码的setTimeout里面的代码是在最后才执行的。这是因为setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式(W3CSchool对SetTImeout的定义)。按照定义来看,执行结果也应该是1;2;3; ,然而实际结果却是1;3;2;。这是为什么呢?这就要从浏览器对代码的执行机制来说起了。

 

那么,来看看下图:

前端的经典的面试题——Event Loop(事件循环)

 

 

(上图转自Philip Roberts的演讲《Help, I'm stuck in an event-loop》)

 

我们来看几个摘自MDN的专业术语

heap(堆):对象被分配在堆中,堆是一个用来表示一大块(通常是非结构化的)内存区域的计算机术语。

stack(栈):函数调用形成了一个由若干帧组成的栈。

WebAPIS:囊括 Web 强大脚本能力的每个 API 参考资料, 包括 DOM 、所有相关的 APIs 及可以用来构建 Web 的相关接口。

队列(event queue):一个 JavaScript 运行时包含了一个待处理消息的消息队列。每一个消息都关联着一个用以处理这个消息的回调函数。

—— 摘自MDN

 

因为javascript是单线程的脚本语言,代码自上而下执行,代码在执行时会被压入执行栈(stack)中,当遇到setTimeout时会将setTimeout函数交给Web API来维护,当异步任务(比如:setTimeout)执行完成后会将对应的回调函数推入事件队列(event queue)中,当执行栈中任务全部执行完成之后浏览器会读取任务队列,把对应的回调函数再压入执行栈中,然后循环执行。这就是所谓的EventLoop。我们再来看看下图,执行结果会是怎样的呢?

前端的经典的面试题——Event Loop(事件循环)

 

执行结果:1;2;3;7;5;4;6;

 

为什么会得到这样的执行结果呢?为什么不是1;2;3;7;4;5;6;呢?这里涉及到了Macrotask 和 Microtask,即宏任务队列和微任务队列。setTimeout属于Macrotask,而Promise属于Microtask,从上图执行结果可以看出Microtask优先级高于Macrotask,当执行栈全部为空时,先询问是否有微任务,如果有,先执行为任务,全部执行完成后再执行宏任务。

 

我们再来看看下面这段代码的执行结果:

前端的经典的面试题——Event Loop(事件循环)

 

执行结果:1;2;3;8;5;6;4;7;

 

与Macrotask有所不同的是,Microtask中的任务不会一个一个压入执行栈中,而是直接压入执行栈,从上图执行结果可以得到佐证。Promise即使放入另外一个Promise的回调函数里,也会先执行Promise的回调,再执行setTimeout的回调。

【免责声明:本文图片及文字信息均由千锋重庆web前端培训小编转载自网络,旨在分享提供阅读,版权归原作者所有,如有侵权请联系我们进行删除。】


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM