浏览器和nodejs事件循环(Event Loop)有什么区别?

devtools/2024/10/21 5:55:43/

单线程和异步

  • JS是单线程的,无论在浏览器还是在nodejs
  • 浏览器中JS执行和DOM渲染共用一个线程,是互斥的
  • 异步是单线程的解决方案

1. 浏览器中的事件循环

异步里面分宏任务和微任务

  • 宏任务:setTimeoutsetIntervalsetImmediateI/OUI渲染,网络请求
  • 微任务:Promiseprocess.nextTickMutationObserverasync/await
  • 宏任务和微任务的区别:微任务的优先级高于宏任务,微任务在当前宏任务结束后、下一个渲染周期前执行。宏任务在下一个事件循环中执行,通常在微任务之后。
    • 宏任务在页面渲染之后执行
    • 微任务在页面渲染之前执行
    • 也就是微任务在下一轮DOM渲染之前执行,宏任务在DOM渲染之后执行

console.log('start')
setTimeout(() => { console.log('timeout')
})
Promise.resolve().then(() => {console.log('promise then')
})
console.log('end')// 输出
// start 
// end 
// promise then
// timeout

// 分析// 等同步代码执行完后,先从微任务队列中获取(微任务队列优先级高),队列先进先出// 宏任务 MarcoTask 队列
// 如setTimeout 1000ms到1000ms后才会放到队列中
const MarcoTaskQueue = [() => {console.log('timeout')},fn // ajax回调放到宏任务队列中等待
]  ajax(url, fn) // ajax 宏任务 如执行需要300ms// ********** 宏任务和微任务中间隔着 【DOM 渲染】 ****************// 微任务 MicroTask 队列
const MicroTaskQueue = [() => {console.log('promise then')}
]// 等宏任务和微任务执行完后 Event Loop 继续监听(一旦有任务到了宏任务微任务队列就会立马拿过来执行)...

<p>Event Loop</p><script>const p = document.createElement('p')p.innerHTML = 'new paragraph'document.body.appendChild(p)const list = document.getElementsByTagName('p')console.log('length----', list.length) // 2console.log('start')// 宏任务在页面渲染之后执行setTimeout(() => {const list = document.getElementsByTagName('p')console.log('length on timeout----', list.length) // 2alert('阻塞 timeout') // 阻塞JS执行和渲染})// 微任务在页面渲染之前执行Promise.resolve().then(() => {const list = document.getElementsByTagName('p')console.log('length on promise.then----', list.length) // 2alert('阻塞 promise') // 阻塞JS执行和渲染})console.log('end')
</script>

2. nodejs中的事件循环

  • nodejs也是单线程,也需要异步
  • 异步任务也分为:宏任务 + 微任务
  • 但是,它的宏任务和微任务分为不同的类型,有不同的优先级
  • 和浏览器的主要区别就是类型优先级,理解了这里就理解了nodejs的事件循环

宏任务类型和优先级

类型分为6个,优先级从高到底执行

  • TimersetTimeoutsetInterval
  • I/O callbacks:处理网络、流、TCP的错误回调
  • Idle,prepare:闲置状态(nodejs内部使用)
  • Poll轮询:执行poll中的I/O队列
  • Check检查:存储setImmediate回调
  • Close callbacks:关闭回调,如socket.on('close')

注意process.nextTick优先级最高,setTimeoutsetImmediate优先级高

执行过程

  • 执行同步代码
  • 执行微任务(process.nextTick优先级最高)
  • 按顺序执行6个类型的宏任务(每个开始之前都执行当前的微任务)

总结

  • 浏览器和nodejs的事件循环流程基本相同
  • nodejs宏任务和微任务分类型,有优先级。浏览器里面的宏任务和微任务是没有类型和优先级的
  • node17之后推荐使用setImmediate代替process.nextTick(如果使用process.nextTick执行复杂任务导致后面的卡顿就得不偿失了,尽量使用低优先级的api去执行异步)
console.info('start')
setImmediate(() => {console.info('setImmediate')
})
setTimeout(() => {console.info('timeout')
})
Promise.resolve().then(() => {console.info('promise then')
})
process.nextTick(() => {console.info('nextTick')
})
console.info('end')// 输出
// start
// end
// nextTick
// promise then
// timeout
// setImmediate

3. nodejs如何开启多进程,进程如何通讯

进程process和线程thread的区别

  • 进程,OS进行资源分配和调度的最小单位,有独立的内存空间
  • 线程,OS进程运算调度的最小单位,共享进程内存空间
  • JS是单线程的,但可以开启多进程执行,如WebWorker

为何需要多进程

  • 多核CPU,更适合处理多进程
  • 内存较大,多个进程才能更好利用(单进程有内存上限)
  • 总之,压榨机器资源,更快、更节省

如何开启多进程

  • 开启子进程 child_process.forkcluster.fork
    • child_process.fork用于单个计算量较大的计算
    • cluster用于开启多个进程,多个服务
  • 使用sendon传递消息

使用child_process.fork方式

const http = require('http')
const fork = require('child_process').forkconst server = http.createServer((req, res) => {if (req.url === '/get-sum') {console.info('主进程 id', process.pid)// 开启子进程 计算结果返回const computeProcess = fork('./compute.js')computeProcess.send('开始计算') // 发送消息给子进程开始计算,在子进程中接收消息调用计算逻辑,计算完成后发送消息给主进程computeProcess.on('message', data => {console.info('主进程接收到的信息:', data)res.end('sum is ' + data)})computeProcess.on('close', () => {console.info('子进程因报错而退出')computeProcess.kill() // 关闭子进程res.end('error')})}
})
server.listen(3000, () => {console.info('localhost: 3000')
})

// compute.js/*** @description 子进程,计算*/function getSum() {let sum = 0for (let i = 0; i < 10000; i++) {sum += i}return sum
}process.on('message', data => {console.log('子进程 id', process.pid)console.log('子进程接收到的信息: ', data)const sum = getSum()// 发送消息给主进程process.send(sum)
})

使用cluster方式

const http = require('http')
const cpuCoreLength = require('os').cpus().length
const cluster = require('cluster')// 主进程
if (cluster.isMaster) {for (let i = 0; i < cpuCoreLength; i++) {cluster.fork() // 根据核数 开启子进程}cluster.on('exit', worker => {console.log('子进程退出')cluster.fork() // 进程守护})
} else {// 多个子进程会共享一个 TCP 连接,提供一份网络服务const server = http.createServer((req, res) => {res.writeHead(200)res.end('done')})server.listen(3000)
}// 工作中 使用PM2开启进程守护更方便

http://www.ppmy.cn/devtools/111134.html

相关文章

pytorch对不同的可调参数,分配不同的学习率

在 PyTorch 中&#xff0c;你可以通过为优化器传递不同的学习率来针对不同的可调参数分配不同的学习率。这通常通过向优化器传递一个字典列表来实现&#xff0c;其中每个字典指定特定参数组的学习率。下面是一个示例代码&#xff0c;展示了如何实现这一点&#xff1a; import …

Python 多线程

开始学习Python线程 线程模块 使用Threading模块创建线程 线程同步 线程优先级队列&#xff08; Queue&#xff09; 多线程类似于同时执行多个不同程序&#xff0c;多线程运行有如下优点&#xff1a; 使用线程可以把占据长时间的程序中的任务放到后台去处理。用户界面可以更…

Pyspark下操作dataframe方法(1)

文章目录 Pyspark dataframe创建DataFrame使用Row对象使用元组与scheam使用字典与scheam注意 agg 聚合操作alias 设置别名字段设置别名设置dataframe别名 cache 缓存checkpoint RDD持久化到外部存储coalesce 设置dataframe分区数量collect 拉取数据columns 获取dataframe列 Pys…

苹果宣布iOS 18正式版9月17日推送:支持27款iPhone升级

9月10日消息&#xff0c;在苹果秋季发布会结束后&#xff0c; 苹果宣布将于9月17日(下周二)推送iOS 18正式版系统。 苹果官网显示&#xff0c;iOS 18正式版将兼容第二代iPhone SE及之后的所有机型&#xff0c;加上刚发布的iPhone 16系列&#xff0c;共兼容27款iPhone。 iOS 18升…

【数据获取与读取】JSON CSV

数据分析流程 获取数据-读取数据-评估数据-清洗数据-整理数据-分析数据-可视化数据 公开数据集 飞桨&#xff08;百度旗下深度学习平台&#xff09;数据集&#xff1a;https:/aistudio.baidu.com/aistudio/datasetoverview 天池&#xff08;阿里云旗下开发者竞赛平台&#xf…

​了解MySQL 的二进制日志文件​Binlog

1. SQL 语句的几种类型 首先介绍一下&#xff0c;对于一个 SQL 语句&#xff0c;它常常被分为以下几种类型&#xff1a; DDL&#xff08;Data Definition Language&#xff0c;数据定义语言&#xff09;&#xff1a;用来操作数据库、表、列等&#xff0c;比如 CREATE、ALTER…

现在有一台ubuntu22.04 的工作站机器,现在想通过RDP的方式进行远程开发

在 Ubuntu 22.04 工作站上通过 RDP&#xff08;远程桌面协议&#xff09;进行连接的具体步骤如下&#xff1a; 1. 安装 RDP 服务 Ubuntu 默认不支持 RDP 连接&#xff0c;因此你需要安装一个 RDP 服务器&#xff0c;通常使用 xrdp 这个软件包。 步骤&#xff1a; 打开终端&a…

核心知识点合集

不断补充... 知识模块文章详情心得&资料JVM相关 Java&#xff1a;简述JDK&#xff0c;JRE&#xff0c;JVM之间的关系JVM&#xff1a;简述JVM内存分配模型JVM&#xff1a;浅谈垃圾回收GC机制JVM&#xff1a;浅谈内存溢出的原因JVM&#xff1a;浅谈JVM调优策略 线程相关资料…