标签:rom 一个个 逻辑 macro 作用域 代码执行 一起 图形 frame
春节的时候看到奇舞周刊发的关于Event Loop的文章https://mp.weixin.qq.com/s/KEl_IxMrJzI8wxbkKti5vg,看的也是迷迷糊糊。
昨天准备写一下几个Promise的小例子,发现理解起来还是要懂得Event Loop,所以又在网上找了几篇文章看了一下,发现各有各的说法,不过关于基础的部分都是一致的,
本着学习记录的心态,我这里简单地把我对Event Loop的理解说明一下,当然还是有很多不到位的地方,欢迎大家一起交流学习。
首先我们来看一张图片:(《Learning TypeScript》)
比如有一个文件test.js
1 function A() { 2 console.log(‘A start‘) 3 Promise.resolve().then( 4 () => console.log(‘A 的 Promise then‘) 5 ) 6 console.log(‘A end‘) 7 } 8 9 function B() { 10 console.log(‘B start‘) 11 setTimeout( 12 () => console.log(‘B 的 setTimeout‘), 13 0 14 ) 15 A(); 16 console.log(‘B end‘) 17 } 18 19 function C() { 20 console.log(‘C start‘) 21 B(); 22 console.log(‘C end‘) 23 } 24 C();
运行结果
在运行到调用C函数时,C函数这个帧会被首先加入到栈中,这时它是栈顶,栈中只有它一个帧。队列此时是空的。
执行C函数(打印出C start)后,发现C函数调用了B函数,这时就把B函数推入栈顶。
执行B函数(打印出B start,将setTimeout的回调函数放在任务队列中,这时任务队列有一个任务)后, 发现B函数调用了A函数,这时就把A函数推入栈顶。
执行A函数,打印出A start ,将Promise的then方法的回调函数放在任务队列中,这时任务队列就有了2个任务。继续执行A函数,直到打印出A end,
这时A函数执行完毕,A函数被弹出栈顶(这时栈中有2个帧 B和C),执行交还给B函数,B函数继续执行,直到打印出B end,
这时B函数执行完毕,B函数被弹出栈顶(这时栈中有1个帧 C),执行交还给C函数,C函数继续执行,直到打印出C end,
这时C函数执行完毕,C函数被弹出栈顶(这时栈中有0个帧,也就是说栈被清空了),
栈被清空之后,系统会去任务队列中找任务(此时任务队列中有2个任务,一个是B函数添加进去的setTimeout的回调函数,一个是A函数添加进去的Promise的then方法的回调函数),
正常情况下,因为队列是先进先出的数据结构,B函数添加进去的setTimeout的回调函数比A函数添加进去的Promise的then方法的回调函数要早(其实也不早,不过这涉及到异步操作的问题,这里暂时这样理解,对于理解Event Loop没有影响)。
理所当然的以为会先打印出B 的 setTimeout,其实并没有,这里涉及到任务的分类,任务分为2类:
很显然,
B函数添加进去的setTimeout的回调函数进入宏任务队列中。A函数添加进去的Promise的then方法的回调函数进入微任务队列中。
需要明确的是,JS的机制是等到栈中为空时,会先去微任务队列寻找任务,根据先进先出的原则把微任务队列中的任务添加到栈中并执行,直到微任务队列为空时才会去宏任务队列中寻找任务。
所以出现了先打印A 的 Promise then 后打印出B 的 setTimeout
下面用图形解释一下(左边是代码执行到的位置,中间是堆栈信息,右边是console打印出来的信息)
上面就是EventLoop最基本的原理,要记住的是
其实上面的只是JS的主线程的逻辑,如果放在浏览器中执行,中间还会有JS线程移交执行权给UI线程渲染页面的过程,这个是另外的话题,之后我会单独写文章再介绍。
2019-02-10
参考:
https://www.cnblogs.com/cangqinglang/p/8967268.html
https://mp.weixin.qq.com/s/KEl_IxMrJzI8wxbkKti5vg
标签:rom 一个个 逻辑 macro 作用域 代码执行 一起 图形 frame
原文地址:https://www.cnblogs.com/wangtingnoblog/p/js_EventLoop.html