事件循环
前提
了解三个部分:
- 执行栈:JS代码只会在执行栈执行,不可能在其他地方执行
- Web API : 浏览器的其他线程(计时、事件监听……)
- 事件队列
举例
下列代码的输出结果是?
const a = 1;
console.log(a);
function A() {
console.log('A');
B();
}
function B() {
console.log('B');
}
A();
答:
3
4
1
3
2
2
过程解析
图一:初始状态
- JS启动之前就会分配一块内存空间(执行引擎干的事),就是执行栈call stack
- JS执行引擎会往执行栈内放入一块
全局上下文
图二:代码从上往下执行
- 第一条:是setTimeout计时的回调函数,执行栈通知浏览器的计时线程开始计时(时间为0秒,计时完成后需要执行的函数是fn)
- 发送通知完成后,setTimeout执行完成,出栈
- 计时结束后(计时线程根据规定的计时器时间来定,与执行栈没有关系),有需要执行的函数,则到执行队列中排队,等待执行栈休息时(空闲时),便会从执行队列中,按顺序执行(同时计时线程完毕)
图三:setTimeout一条语句执行完后的的结果:回到全局上下文
图四:代码继续从上往下执行
- 函数声明不用理会
- 第二条语句执行:a();
a();
- 形成函数a的上下文,函数a上下文入栈,函数a里面继续执行,执行setTimeout的回调函数
- 执行栈通知浏览器的计时线程开始计时(时间为0秒,计时完成后需要执行的函数是fn2)
- 发送通知完成后,setTimeout执行完成,出栈
- 计时结束后,有需要执行的函数,到执行队列中排队,等待执行栈休息时,便会从执行队列中,按顺序执行(同时计时线程完毕)
图五:继续执行函数a里的语句
- 执行log语句,产生log上下文,则log上下文入栈
图六:输出3
- log语句执行完毕,log上下文出栈
图七:函数a完毕执行完毕,函数a上下文执行完毕,出栈
图八:回到全局上下文,继续往下执行
- 执行语句:
console.log(4);
- 触发log上下文,入栈
图九:执行完 log语句后,后输出4
- log上下文出栈
图十:回到全局上下文,全局中所有语句执行完毕,则全局上下文出栈,执行栈空闲,可以休息
图十一:执行栈休息中
- 执行栈休息时,会从执行队列中拿取需要执行的函数,进行执行
- 从队列头开始拿,fn
图十二:执行栈准备执行函数fn,则触发fn上下文入栈,fn2自动补齐到队列头
图十三:执行函数fn,从上往下执行
- fn函数内的第一条语句是:console.log(1),触发log上下文入栈
图十四:输出1,log执行完毕,log上下文执行完毕,出栈
图十五:执行函数fn,从上往下执行
- fn函数内的第二条语句是:a(),触发函数a上下文入栈
图十六:函数a里面继续执行,执行setTimeout
- 执行栈通知浏览器的计时线程开始计时(时间为0秒,计时完成后需要执行的函数是fn3),
- 发送通知完成后,setTimeout执行完成,出栈
- 计时结束后,有需要执行的函数,到执行队列中排队,等待执行栈休息时,便会从执行队列中,按顺序执行(同时计时线程完毕)
图十七:继续执行函数a内的语句:console.log(3);触发log上下文入栈
图十八:输出3,执行完log语句后,log上下文出栈
- 函数a也执行完毕,函数a上下文也出栈
- 自此:fn函数也执行完毕,fn上下文出栈
图十九:同上可得,fn上下文出栈后
- 执行栈休息时,会又从执行队列中拿取队列头的fn2函数,进行行执行
- fn2上下文入栈,执行fn2函数语句,console.log(2),触发log语句,log上下文入栈
- 输入2后,log执行完毕,log上下文出栈
图二十:fn2函数执行完毕后,fn2上下文出栈
- 执行栈空闲,继续从执行队列中拿取等待执行函数fn3,fn3上下文入栈
图二十一:执行完log语句,log上下文出栈
图二十二:函数fn3执行完毕,fn3上下文出栈
图二十三:执行栈空闲,执行队列也空闲,则执行栈休息
课堂作业
输出结果是?
setTimeout(() => {
console.log(1);
}, 0);
const start = Date.now();
while (Date.now() - start < 1000) {}
console.log(2);
答:2 1
自主分析过程哟~