浏览器宿主环境中包含五个线程:
1、JS引擎:负责执行执行栈的最顶部代码;
2、GUI线程:负责渲染页面(html,css)
3、事件监听线程:负责监听各种事件
4、计时线程:负责计时(定时器)
5、网络线程:负责网络通信(ajax)
事件循环概念(抽象的术语):当浏览器的线程发生了某些事情,如果该线程发现这件事情有处理程序,它会将该处理长须加入一个叫做事件队列的内存。当JS引擎发现执行栈中已经没有了任何内容后,会将事件队列中的第一个函数加入到执行栈中执行。JS引擎对事件队列的取出执行方式,以及与宿主环境的配合,称之为事件循环
事件循环:执行栈--->浏览器宿主环境----->事件队列---->执行栈
例子:demo.js
console.log("a");
setTimeout(()=>{
console.log("b");
})
for(let i=0;i<1000;i++){
console.log("c")
}
js执行的结果 a ,...c(1000个c) ,b
解释:
1、执行栈中:(1)创建一个全局上下文,
(2)然后创建一个log()函数上下文,
(3)执行完后打印a,
(4)销毁log()函数的上下文;
(5)创建一个setTimeout()函数的上下文
2、浏览器宿主环境:(6)通知计时线程来计时0秒后运行异步函数
3、事件队列(必须等执行栈中清空后再把队列中等待的函数放到执行栈中执行):(7)把异步函数放入事件队列排队等待
4、回到执行栈:(8)销毁setTimeout()函数的上下文
(9)执行for循环输出1000个c,然后销毁循环,销毁全局上下文,执行栈清空了
(10)看事件队列是否有东西,有东西则拿出来放到执行栈中执行,创建异步函数上下文,
销毁异步函数上下文【如果事件队列中有多个则一个一个拿出来创建销毁】
所有最终的输出结果才会是:a,1000个c,b
补充:
事件队在不同的宿主环境中有所差异,大部分宿主环境会将事件对列进行细分。在浏览器中,事件对列表列分为【宏队列,微队列】
【宏队列】:macrotask,计时器结束的回调,事件的回调,http回调等等绝大部分异步函数进入宏队列
【微队列】:MutationObserve,promise产生的回调进入微队列
当执行栈清空时,JS引擎首先会将微队列任务中的所有任务一次执行结束,如果没有微队列任务,则执行宏队列