JavaScript 单线程指的是浏览器中负责解释和执行 JavaScript 代码的只有一个线程,即为JS引擎线程
JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。
也称为JS内核,负责处理JavaScript脚本。(例如V8引擎) ①JS引擎线程负责解析JS脚本,运行代码。 ②JS引擎一直等待着任务队列中的任务的到来,然后加以处理。 ③一个Tab页(renderer进程)中无论什么时候都只有一个JS线程运行JS程序。 |
归属于渲染进程而不是JS引擎,用来控制事件循环 ①当JS引擎执行代码块如setTimeout时(也可来自浏览器内核的其他线程,如鼠标点击、Ajax异步请求等),会将对应任务添加到事件线程中。 ②当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。 注意:由于JS的单线程关系,所以这些待处理队列中的事件都是排队等待JS引擎处理,JS引擎空闲时才会执行。 |
①浏览器定时计数器并不是由JS引擎计数的。 ②JS引擎时单线程的,如果处于阻塞线程状态就会影响计时的准确,因此,通过单独的线程来计时并触发定时。 ③计时完毕后,添加到事件队列中,等待JS引擎空闲后执行。 注意:W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。 |
XMLHttpRequest在连接后通过浏览器新开一个线程请求 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调放入事件队列中,再由JS引擎执行。 |
负责渲染浏览器界面,包括: ②重绘(Repaint)以及回流(Reflow)处理。 |
“任务队列”是一个事件的队列(也可以理解成消息的队列)
任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。
同步任务 指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务 指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
“任务队列”是一个先进先出(FIFO)的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,只要执行栈一清空,“任务队列”上第一位的事件就自动进入主线程。但是,由于存在后文提到的定时器功能,主线程首先要检查一下执行时间,某些事件只有到了规定的时间,才能返回主线程。
主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。
如果执行栈里的任务执行完成,即执行栈为空的时候(即JS引擎线程空闲),事件触发线程才会从消息队列取出一个任务(即异步的回调函数)放入执行栈中执行。
就算延时为 0ms,只是 timer 2 的回调函数会立即加入消息队列而已,回调的执行还是得等执行栈为空(JS引擎线程空闲)时执行。
其实 setTimeout 的第二个参数并不能代表回调执行的准确的延时事件,它只能表示回调执行的最小延时时间,因为回调函数进入消息队列后需要等待执行栈中的同步任务执行完成,执行栈为空时才会被执行。
浏览器端事件循环中的异步队列有两种:macro(宏任务)队列和 micro(微任务)队列。宏任务队列可以有多个,微任务队列只有一个。
有些地方会列出来UI Rendering,说这个也是宏任务 可是在读了HTML规范文档以后,发现这很显然是和微任务平行的一个操作步骤 |
|
|
|
requestAnimationFrame姑且也算是宏任务吧,requestAnimationFrame在为,下次页面重绘前所执行的操作,而重绘也是作为宏任务的一个步骤来存在的,且该步骤晚于微任务的执行
Promise中注意是回调,而new Promise在实例化的过程中所执行的代码都是同步进行的
直到 2 秒后,主线程中的任务才执行完成,这才去执行 macrotask 中的 setTimeout 回调任务。
Node.js也是单线程的Event Loop,但是它的运行机制不同于浏览器环境
JS的运行环境主要有两个:浏览器、Node。
在两个环境下的Event Loop实现是不一样的,在浏览器中基于规范来实现,不同浏览器可能有小小区别。在Node中基于libuv这个库来实现。
浏览器环境下,microtask的任务队列是每个macrotask执行完之后执行。而在Node.js中,microtask会在事件循环的各个阶段之间执行,也就是一个阶段执行完毕,就会去执行microtask队列的任务。
浏览器和Node 环境下,microtask 任务队列的执行时机不同:
先执行宏任务(当前代码块也算是宏任务),然后执行当前宏任务产生的微任务,然后接着执行宏任务
这道题跟上面题目不同之处在于,执行代码会产生很多个宏任务,每个宏任务中又会产生微任务
由于在执行microtask任务的时候,只有当microtask队列为空的时候,它才会进入下一个事件循环,因此,如果它源源不断地产生新的microtask任务,就会导致主线程一直在执行microtask任务,而没有办法执行macrotask任务,这样我们就无法进行UI渲染/IO操作/ajax请求了,因此,我们应该避免这种情况发生。在nodejs里的process.nexttick里,就可以设置最大的调用次数,以此来防止阻塞主线程。
nodejs并不是真正的单线程架构,其中还有I/O线程,这些线程是由更底层的libuv/
事件循环允许Node.js执行非阻塞I/O操作.尽管JavaScript是单线程的.通过尽可能将操作卸载到系统内核。由于大多数现代内核都是多线程的,因此它们可以处理在后台执行的多个操作。当其中一个操作完成时,内核会告诉Node.js,以便可以将相应的回调添加到轮询队列中以最终执行
EventLoop 中的 6个步骤都是相对于宏任务(Macro)的讲述的。NodeJs执行完成一个宏任务后会立即清空当前队列中产生的所有微任务。
上一次循环队列中还未执行完毕的会在这个阶段执行,比如延迟到下一个Loop之中的I/O操作,
尽在NodeJs内部调用,无法操作
如果 轮询 队列 不是空的 ,事件循环将循环访问回调队列并同步执行它们,直到队列已用尽,或者达到了与系统相关的硬性限制。
setImmediate()
调度,则事件循环将等待回调被添加到队列中,然后立即执行。
poll可以提前结束使事件循环,提升效率
这里打印输出出来的结果,并没有什么固定的先后顺序,偏向于随机。(event loop启动需要时间)
如果没有到1ms
,那么在timers
阶段的时候,下限时间没到,setTimeout
回调不执行,事件循环来到了poll
阶段,这个时候队列为空,于是往下继续,先执行了setImmediate()的回调函数,之后在下一个事件循环再执行setTimemout
的回调函数。
问题总结:而我们在==执行启动代码==的时候,进入timers
的时间延迟其实是==随机的==,并不是确定的,所以会出现两个函数执行顺序随机的情况。
比如有个客户端请求A进来,需要读取文件,读取文件后将内容整合,最后数据返回给客户端。但在读取文件的时候另一个请求进来了,那处理的流程是怎么样的?
JSON.parse(JSON.stringify(bigObj))
cluster 模块可以创建共享服务器端口的子进程
在一个进程的前提下开启多个线程
可开辟线程执行大量计算,返回结果
node 具有异步 I/O 特性,每当有 I/O 请求发生时,node 会提供给该请求一个 I/O 线程。然后 node 就不管这个 I/O 的操作过程了,而是继续执行主线程上的事件,只需要在该请求返回回调时再处理即可。也就是 node 省去了许多等待请求的时间
node中每一个模块都有一个自己的module对象
require
过,不会重复执行加载,直接可以拿到里面的接口对象
版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。