1.JS为什么是单线程?
进程:正在运行中的应用程序。每个进程都自己独立的内存空间。例如:打开的浏览器就是一个进程。
线程:进程的子集,是独立的。线程在共享的内存空间中运行。
javascript语言在设计之时有一个十分重要的需求需要实现,那就是操作DOM节点,设想一下假如JS是多线程运行,此时线程1中代码将DOM_1节点设置为隐藏,线程2中代码将DOM_1节点设置为显示,那么此时界面中到底会被渲染成什么样呢?究竟是隐藏节点还是显示节点呢?在茫茫多的DOM操作上都可能有冲突操作,增加了程序的复杂性,所以为了避免出现类似问题,JS被设计成为单线程执行,最新的HTML5中新增的Web-Worker是一个被阉割的多线程,如果你有尝试使用过,你可能会知道在Web-Worker中无法操作DOM节点。
2.单线程带来的问题?
单线程运行好比咱们现在排队做核酸!大家排队依次打开健康码,待工作人员扫码录入后领取棉签,随后到医生那做核酸采样。每次前面的人做完,后面的人才能继续做,但是有的时候前面一位已经做完,后面这位的健康码因为网络原因,设备原因迟迟无法打开,会导致后面几十人都被耽误,这就是单线程运行带来问题,如何解决这个问题?工作人员会讲这位同志请离队伍,等他在边上将健康码打开完毕以后再将他重新塞入队伍进行核酸采样,在JS单线程运行机制中设计者也用类似的规则来处理任务,这个规则叫做事件循环。
3.JS的事件循环。
当我们编写好一段JS代码,他会如何执行?JS引擎将可执行代码由上至下依次入栈(调用栈)执行,首先会判断执行函数为同步执行函数还是异步执行函数,假如为同步任务,则直接进入主线程运行,当运行完毕并返回结果再开始下一个,如果为异步任务则会将这个执行函数塞入一个 event table中执行,并注册回调函数,不等待运行完毕就继续开始下一个任务(不等待避免阻塞),当这个异步任务执行完毕,此任务回调函数被塞入任务队列。当主线程的同步任务全部执行完毕,JS引擎会去任务队列中拿取一个任务置入主线程执行,当主线程任务再次执行完毕,JS引擎会再次去到任务队列拿取并执行,如此反复构成事件循环,推动JS代码不断运行。
4.关于单线程与异步任务的理解。
为什么说JS是单线程但是同时又能够做其他事情,比如定时器,http请求等,那是因为其宿主对象(浏览器进程)开启了多个线程来处理这些事情,包含的线程如下:
1.JS引擎线程:
“JavaScript 引擎”通常被称作一种虚拟机。也称为JS内核,这个线程就是我们平时说的单线程,负责处理Javascript脚本程序。(例如V8引擎),
• JS引擎线程负责解析Javascript脚本,运行代码。
• JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序。
2.GUI渲染线程:
- 负责渲染页面,解析HTML,CSS,构建DOM tree,CSS tree。
- 当界面需要重绘 repaint,重排reflow(回流)时该线程就会执行(重排重绘,像极了顶点着色器和片元着色器)
- 注意这里GUI渲染线程和JS引擎线程互斥,一个运行另外一个就会挂起,这是必要的,这是为了避免DOM操作和呈现上的冲突,他们不能同时运行,而且他们中的一方执行的时间过长的话,用户会感受到卡顿,因为他们相互阻塞。
3.事件触发线程
当事件符合触发条件被触发时,该线程会把对应的事件回调函数添加到消息队列的队尾,等待JS引擎的处理。
4.定时触发器线程
浏览器定时计数器并不是由JS引擎计数的,因为JS引擎是单线程的,如果JS引擎线程处于阻塞线程状态就会影响计数的准确。因此通过另外独立的线程来计时并触发定时是更为合理的方案。
5、异步http请求线程
http请求是在XMLHttpRequest连接后通过浏览器新开一个线程请求。
请求完成有结果之后,将检测到状态变更,如果设置有回调函数,异步http请求线程就会将该请求的回调函数添加到消息队列中,等待JS引擎处理。
异步任务在如何执行:1.通知任务开始执行这个步骤在JS主线程中执行。2.任务执行在对应的线程中完成。3.任务执行完毕,将其回调函数置入任务队列,等待主线程拉取执行,注意异步任务有微任务和宏任务之分,简单理解为promise相关为微任务,他的优先级会在宏任务之前,JS始终会先执行微任务,再执行宏任务。
盗个图
image.png
网友评论