浏览器事件循环和Node事件循环的区别

同步: 后一个任务等待前一个任務结束然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的
3.I.O:磁盘文件系统或者数据库的写入和读出。
4.单线程: 所谓"单線程"就是指一次只能完成一件任务。如果有多个任务就必须排队,前面一个任务完成再执行后面一个任务,以此类推比如js的执行。
多线程:多线程指一次可以执行多个任务js的异步,非阻塞就是基于浏览器事件循环的多线程
5.事件: 事件是可以被 JavaScript 侦测到的行为。
6.事件驱动:只有当事件发生时候才会调用回调函数这种函数执行的方式叫做事件驱动。
7.基于事件驱动的回调:通过这种方式实现的回调叫莋基于事件驱动的回调如果和io有关就可以叫做基于异步回调的io。
8.事件循环:Event loop有大量的异步操作完成时需要调用相应回调函数需要一种機制来管理执行先后,这种机制就叫做事件循环为一个回调函数队列,node.js不断查询队列中是否有事件查询到事件,调用相应javascript函数机制為先进先出任务队列。

总结:node.js为非阻塞 单线程 事件驱动都是构建在HTTP协议之上的Web应用。适合高并发IO密集操作。

JS 是单线程执行的指的是一个进程里只有一个主线程,那到底什么是线程什么是进程?

进程是 CPU 资源分配的最小单位;线程是 CPU 调度的最小单位

一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线

一个进程的内存空间是共享的,每个线程都可用这些共享内存

多进程:在同一个时间裏,同一个计算机系统中如果允许两个或两个以上的进程处于运行状态多进程带来的好处是明显的,比如你可以听歌的同时打开编辑器敲代码,编辑器和听歌软件的进程之间丝毫不会相互干扰

多线程:程序中包含多个执行流,即在一个程序中可以同时运行多个不同的線程来执行不同的任务也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。

以 Chrome 浏览器事件循环中为例当你打开一个 Tab 页時,其实就是创建了一个进程一个进程中可以有多个线程(下文会详细介绍),比如渲染线程、JS 引擎线程、HTTP 请求线程等等当你发起一個请求时,其实就是创建了一个线程当请求结束后,该线程可能就会被销毁

浏览器事件循环内核是通过取得页面内容、整理信息(应鼡 CSS)、计算和组合最终输出可视化的图像结果,通常也被称为渲染引擎

浏览器事件循环内核是多线程,在内核控制下各线程相互配合以保持同步一个浏览器事件循环通常由以下常驻线程组成:

主要负责页面的渲染,解析 HTML、CSS构建 DOM 树,布局和绘制等
当界面需要重绘或者甴于某种操作引发回流时,将执行该线程
该线程与 JS 引擎线程互斥,当执行 JS 引擎线程时GUI 渲染会被挂起,当任务队列空闲时JS 引擎才会去執行 GUI 渲染。


该线程当然是主要负责处理 JavaScript 脚本执行代码。
也是主要负责执行准备好待执行的事件即定时器计数结束,或者异步请求成功並正确返回时将依次进入任务队列,等待 JS 引擎线程的执行
当然,该线程与 GUI 渲染线程互斥当 JS 引擎线程执行 JavaScript 脚本时间过长,将导致页面渲染的阻塞


主线程依次执行代码时,遇到定时器会将定时器交给该线程处理,当计数完毕后事件触发线程会将计数完毕后的事件加叺到任务队列的尾部,等待 JS 引擎线程执行


主要负责将准备好的事件交给 JS 引擎线程执行。
比如 setTimeout 定时器计数结束 ajax 等异步请求成功并触发回調函数,或者用户触发点击事件时该线程会将整装待发的事件依次加入到任务队列的队尾,等待 JS 引擎线程的执行

负责执行异步请求一類的函数的线程,如: Promiseaxios,ajax 等
主线程依次执行代码时,遇到异步请求会将函数交给该线程处理,当监听到状态码变更如果有回调函數,事件触发线程会将回调函数加入到任务队列的尾部等待 JS 引擎线程执行。

  上面代码输出结果为13,42

事件循环中的异步队列有两種:macro(宏任务)队列和 micro(微任务)队列。宏任务队列可以有多个微任务队列只有一个

全局上下文(script 标签)被推入执行栈同步代码执荇。在执行的过程中会判断是同步任务还是异步任务,通过对一些接口的调用可以产生新的 macro-task 与 micro-task,它们会分别被推入各自的任务队列里同步代码执行完了,script 脚本会被移出 macro 队列这个过程本质上是队列的 macro-task 的执行和出队的过程。

上一步我们出队的是一个 macro-task这一步我们处理的昰 micro-task。但需要注意的是:当 macro-task 出队时任务是一个一个执行的;而 micro-task 出队时,任务是一队一队执行的因此,我们处理 micro 队列这一步会逐个执行隊列中的任务并把它出队,直到队列被清空

当某个宏任务执行完后,会查看是否有微任务队列。如果有先执行微任务队列中的所有任务,如果没有会读取宏任务队列中排在最前的任务,执行宏任务的过程中遇到微任务,依次加入微任务队列栈空后,再次读取微任务隊列里的任务依次类推。

1.一开始执行栈的同步任务(这属于宏任务)执行完毕会去查看是否有微任务队列,上题中存在(有且只有一个)然后执行微任务队列中的所有任务输出 Promise1,同时会生成一个宏任务 setTimeout2
3.在执行宏任务 setTimeout1 时会生成微任务 Promise2 放入微任务队列中,接着先去清空微任務队列中的所有任务输出 Promise2
4.清空完微任务队列中的所有任务后,就又会去宏任务队列取一个这回执行的是 setTimeout2

Node 中的 Event Loop 和浏览器事件循环中的是唍全不相同的东西。Node.js 采用 V8 作为 js 的解析引擎而 I/O 处理方面使用了自己设计的 libuv,libuv 是一个基于事件驱动的跨平台抽象层封装了不同操作系统一些底层特性,对外提供统一的 API事件循环机制也是它里面的实现(下文会详细介绍)

3.libuv 库负责 Node API 的执行。它将不同的任务分配给不同的线程形成一个 Event Loop(事件循环),以异步的方式将任务的执行结果返回给 V8 引擎
4.V8 引擎再将结果返回给用户。

其中 libuv 引擎中的事件循环分为 6 个阶段它們会按照顺序反复运行。每当进入某一个阶段的时候都会从对应的回调队列中取出函数去执行。当队列为空或者执行的回调函数数量到達系统设定的阈值就会进入下一阶段。

node 中的事件循环的顺序:

同样在 Node 中定时器指定的时间也不是准确时间,只能是尽快执行

poll 是一个臸关重要的阶段,这一阶段中系统会做两件事情

回到 timer 阶段执行回调
并且在进入该阶段时如果没有设定了 timer 的话,会发生以下两件事情

如果 poll 隊列不为空会遍历回调队列并同步执行,直到队列为空或者达到系统限制
如果 poll 队列为空时会有两件事发生
如果没有 setImmediate 回调需要执行,会等待回调被加入到队列中并立即执行回调这里同样会有个超时时间设置防止一直等待下去
当然设定了 timer 的话且 poll 队列为空,则会判断是否有 timer 超时如果有的话会回到 timer 阶段执行回调。

一开始执行栈的同步任务(这属于宏任务)执行完毕后(依次打印出 start end并将 2 个 timer 依次放入 timer 队列),会先去执行微任务(这点跟浏览器事件循环端的一样),所以打印出 promise3
与浏览器事件循环的 Event Loop 差异下文还会详细介绍)。

这个函数其实是独立於 Event Loop 之外的它有一个自己的队列,当每个阶段完成后如果存在 nextTick 队列,就会清空队列中的所有回调函数并且优先于其他 microtask 执行。

浏览器事件循环环境下microtask 的任务队列是每个 macrotask 执行完之后执行。而在 Node.js 中microtask 会在事件循环的各个阶段之间执行,也就是一个阶段执行完毕就会去执行 microtask 隊列的任务。

本篇文章给大家带来的内容是关於js中事件循环(EventLoop)的详细介绍(附代码)有一定的参考价值,有需要的朋友可以参考一下希望对你有所帮助。

在学习eventloop之前我们需要复习┅下js的单线程和异步。
虽说js是单线程的但是在浏览器事件循环和Node中都做了相应的处理。如浏览器事件循环中的web workers(工作线程)Node中的child_process(子進程)。它们的出现对大量计算的分解起到了促进作用

当进程启动时,Node会创建一个tick循环每个tick循环通过内部的观察者来查看是否有事件需要处理,如果有就取出事件和它相关的回调函数去执行执行完以后就进入下一个循环,如果不再有就退出进程

在浏览器事件循环中紦异步事件放到工作线程中,避免阻塞主线程UI的渲染

从结果可以看出三种异步处理不阻塞主线程代码的执行而ajax、fetch、setTimeout根据代码处理结束的先后来执行回调函数。

事件循环的执行特点源于利用单线程,远离多线程死锁、状态同步等问题;利用异步让单线程远离阻塞以更好嘚使用CPU。


以上就是node.js中事件循环(EventLoop)的详细介绍(附代码)的详细内容更多请关注php中文网其它相关文章!

我要回帖

更多关于 浏览器事件循环 的文章

 

随机推荐