JavaScript中异步同步编程知识点
前言
浏览器最小反应时间
即便给定时器的等待时间设置为零,也不是立即执行,浏览器都有一个最小的等待时间。谷歌大概5ms左右,IE浏览器10ms左右
获取代码执行所需要的时间
<script>
console.time("for 循环测试");
for (let i = 0; i < 1000000000; i++) { // 代码部分
}
console.timeEnd("for 循环测试");
</script>
代码执行结果
这个代码执行时间需要受到电脑配置、和当前电脑运行环境等多方面因素影响,只可以作为参考
进程/线程
进程process
进程代表的是一个程序
电脑端安装很多的应用软件,每当运行一个应用程序,都相当于开辟了一个进程
对于浏览器来说,每新建一个或者访问一个页面,都是新开辟一个进程
线程thread
线程是程序中需要处理的事情,如果程序中需要同时处理很多事情,则需要开辟多个线程
一个线程同时只能做一个事情
一个进程中,会包含多个线程
浏览器常用线程
浏览器的内核是多线程的,它们在内核制控下相互配合以保持同步,一个浏览器至少实现三个常驻线程:javascript引擎线程,GUI渲染线程,浏览器事件触发线程。
javascript引擎是基于事件驱动单线程执行的.JS引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JS线程在运行JS程序。
当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。但需要注意 GUI渲染线程与JS引擎是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。这些事件可来自JavaScript引擎当前执行的代码块如setTimeOut、也可来自浏览器内核的其他线程如鼠标点击、AJAX异步请求等,但由于JS的单线程关系所有这些事件都得排队等待JS引擎处理。
JS渲染是单线程的
异步编程机制
1.JS是单线程,所以在“栈”中,代码一定是按照顺序,自上而下依次执行的
2.代码执行过程中如果遇到一个异步的操作代码:
(1)定时器:设置定时器的操作是同步的,异步指的是间隔多久后执行指定的函数
(2)事件绑定(监听)
(3)ajax的异步请求
(4)promise/async/await
事件队列 EventQueue:
3.当遇到定时器之后:浏览器默认开辟一个事件队列 EventQueue
4.JS代码执行过程中遇到的异步操作,都先放置到事件队列中:浏览器会单独开辟一个 定时器线程:
监听事件队列中存储的各个定时器的到达时间
只有GUI线程空闲下来,才会过来找
5.此时GUI渲染线程继续向下执行代码
6.遇到循环:循环是同步的,代码中遇到死循环,会中断整个页面的循环或者中断代码的执行
7.继续执行同步代码
事件循环 EventLoop:
8.栈内存中的同步任务代码都执行完了
去事件队列中找到达时间的任务
把找到的已经到达执行条件的任务,放入到栈中执行(每次只能拿回来一个)
9.等GUI执行完,“闲下来”,在去事件队列中找其他到达时间的任务…一直到事件队列被执行完为止
这个循环查找的过程就是EventLoop事件循环
总结:
通常一个浏览器会至少存在三个线程:JS引擎线程(用于处理JS)、GUI渲染线程(用于页面渲染)、浏览器时间触发线程(用于控制交互)。
而因为JS可以操作DOM元素,进而会影响到GUI的渲染结果,因此JS引擎线程与GUI渲染线程是互斥的。也就是说当JS引擎线程处于运行状态时,GUI渲染线程将处于冻结状态。
JS引擎是基于事件驱动,采用的是单线程运行机制。即JS引擎会只会顺序的从任务列表中取任务,并执行。
SetTimeout/SetInternal
其SetTiemout:在指定的毫秒数后调用指定的代码段;SetInternal:在指定的时间间隔内(ms)循环调用指定的代码段。这两个函数内都涉及到时间计数器,也就是都涉及到一个类似与MFC定时器。JS引擎本身就只能单线程运行,因此定时器需要由其他的外部线程来启动。所以对JS引擎而言,定时器线程可以被视为异步线程。但当定时器时间到达后,所触发的事件则必须在任务列表中排队,等候JS引擎的处理。