「前端· 2023」入门 Node 问题总结

Node 事件循环和浏览器端的差异

在浏览器环境下,microtask(微任务)的任务队列,在每个 macrotask(宏任务)执行完之后执行。

在 node 中,microtask(微任务)会在事件循环的各个阶段之间执行,也就是一个阶段执行完毕,就会去执行清空 microtask 队列的任务。

在 node 中,setTimeout、setInterval、 setImmediate、script(整体代码)、 I/O 操作,属于宏任务。new Promise().then(回调) 属于微任务。process.nextTick 优先级大于微任务,当每个阶段完成后,就会清空 process.nextTick。

js 复制代码
// 在浏览器环境之中
// 1 2 3 4
setTimeout(()=>{
    console.log('1')
    Promise.resolve().then(function() {
        console.log('2')
    })
}, 0)

setTimeout(()=>{
    console.log('3')
    Promise.resolve().then(function() {
        console.log('4')
    })
}, 0)
js 复制代码
// 在 node 环境之中,两个 timerout,添加到 timer 队列中,清空 timer 队列,然后清空微任务队列
// 1 3 2 4
setTimeout(()=>{
    console.log('1')
    Promise.resolve().then(function() {
        console.log('2')
    })
}, 0)

setTimeout(()=>{
    console.log('3')
    Promise.resolve().then(function() {
        console.log('4')
    })
}, 0)

进程和线程的区别

进程

  1. 进程是程序的实体
  2. 每一个进程相互独立
  3. 线程是进程的基本执行单位
  4. 操作系统可以执行多个进程。但是CPU一次只能执行一个进程,CPU通过快速切换执行进程实现多个进程的执行。(多核CPU可以真正的实现多个进程的执行)
  5. 一个进程可以创建其他进程,这些进程被称为子进程 6, 进程不与其他进程之间共享内存
  6. 进程的切换销毁需要的资源更多

线程

  1. 同一个进程下的线程会共享内存,不同的进程不会共享内存
  2. 同一个进程中,可能会有多个线程
  3. 线程切换销毁的资源更少

Node 事件循环

Node10 及以前,事件循环和浏览器有所差异。Node11 之后 Node 的事件循环和浏览器中一致

shell 复制代码
   ┌───────────────────────────┐
┌─>│           timers          │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │     pending callbacks     │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │       idle, prepare       │
│  └─────────────┬─────────────┘      ┌───────────────┐
│  ┌─────────────┴─────────────┐      │   incoming:   │
│  │           poll            │<─────┤  connections, │
│  └─────────────┬─────────────┘      │   data, etc.  │
│  ┌─────────────┴─────────────┐      └───────────────┘
│  │           check           │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
└──┤      close callbacks      │
   └───────────────────────────┘
  1. timers: 执行 setTimeout 和 setInterval 的回调
  2. pending callbacks: 执行推迟到下一次循环迭代的 I/O 回调。
  3. idle, prepare: node内部使用
  4. poll: 执行 IO 相关的回调
  5. check: 执行 setImmediate 的回调
  6. close callbacks: 执行关闭事件的回调,比如:socket.on('close', ...)

timers

定时器指定了一个阀值,阀值到了之后,会执行定时器的回调。但是 node 可能不是特别准确,只能说对于定时器回调,将在指定的时间过后尽可能早地运行。

在 timers 阶段,timers 队列中无论有多少个回调都会依次执行。并不像浏览器端,每执行一个宏任务后就去清空微任务。node 会先清空 timer 队列,然后清空微任务队列。

js 复制代码
// 由于定时器可能不是特别准确,所以打印的顺序是不一定的
setTimeout(() => {
  console.log('timeout')
}, 0)

setImmediate(() => {
  console.log('immediate')
})

poll

处理 IO 相关的回调队列。even loop 将同步执行 poll 队列里的回调,直到队列为空。接下来 even loop 会去检查有没有 setImmediate,如果有 setImmediate, event loop 将结束 poll 阶段进入 check 阶段,并执行 check 阶段的任务队列。

如果没有 setImmediate,event loop 将阻塞在该阶段等待。同时 event loop 会有一个检查机制,检查 timer 队列是否为空,如果 timer 队列非空,会再次进入timer 阶段。

所以在处理完 poll 队列之后,setImmediate 先执行,之后是 setTimeout。

js 复制代码
const fs = require('fs')

// setImmediate先执行
fs.readFile(__filename, () => {
    setTimeout(() => {
        console.log('timeout');
    }, 0)

    setImmediate(() => {
        console.log('immediate')
    })
})

check

setImmediate 的回调会被加入 check 队列中, event loop 处理 check 队列。

setTimeout 和 setImmediate

  • setImmediate 设计在 poll 阶段完成时执行,即 check 阶段
  • setTimeout 设计在 poll 阶段为空闲时,且设定时间到达后执行,它在 timer 阶段执行

process.nextTick

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

js 复制代码
// 1. poll 阶段
// 2. poll 阶段之后清空 nextTick 队列,打印:nextTick1,nextTick2,nextTick3,nextTick4
// 3. timers 阶段 打印:timer1
// 4. timers 阶段之后清空微任务,打印:promise1

// 打印的顺序:
// nextTick1
// nextTick2
// nextTick3
// nextTick4
// timer1
// promise1
setTimeout(() => {
    console.log('timer1')
    Promise.resolve().then(function() {
        console.log('promise1')
    })
}, 0)

process.nextTick(() => {
    console.log('nextTick1')
    process.nextTick(() => {
        console.log('nextTick2')
        process.nextTick(() => {
            console.log('nextTick3')
            process.nextTick(() => {
                console.log('nextTick4')
            })
        })
    })
})

什么是 NPX ?

  • NPM, Node 包管理器, NPM 内置在 Node.js 中,通过命令行工具 CLI 来和线上 NPM 数据库进行交互,这个数据库被称为 NPM Register

  • NPX, Node 包执行器, 该 Node 包可以是本地也可以是远程的。允许开发者在无需安装的情况下执行任意 Node 包

    • 执行本地 Node 包时,NPX 会到node_modules/.bin路径和环境变量 $PATH 里面,检查命令是否存在
    • 执行远程 Node 包时,NPX 会将 Node 包下载到一个临时目录中,使用以后再删除
  • 使用 NPM 执行一个包

      1. 按照到本地,npm install package_name
      1. 执行包 ./node_modules/.bin/package_name(或者使用 npm run package-name 执行)
  • 使用 NPX 执行一个包

      1. 直接执行 npx your-package-name

总结: NPM 是一个 Node 包管理器,NPX 是一个 Node 包执行器。包的执行也可以 NPM 来完成,但是必须进行本地安装,通过定位本地路径或者配置 scripts 来能执行。NPX 则通过一个简单命令大大简化了包运行的成本,既可以运行本地包,也可以远程包,无需安装包也可以执行该包,这就有效避免了本地磁盘污染的问题,节省了本地磁盘空间。

什么是 PNPM ?

  1. pnpm 本质上就是一个包管理器, 同 npm/yarn 一样。但是速度会比 npm/yarn 快 2, 3 倍。
  2. 同时占用磁盘空间小。用 npm/yarn 的时候,如果 100 个项目都依赖 lodash,那么 lodash 很可能就被安装了 100 次。但在使用 pnpm 只会安装一次,磁盘中只有一个地方写入,后面再次使用都会直接使用 hardlink。即使一个包的不同版本,pnpm 也会极大程度地复用之前版本的代码。举个例子,比如 lodash 有 100 个文件,更新版本之后多了一个文件,那么磁盘当中并不会重新写入 101 个文件,而是保留原来的 100 个文件的 hardlink,仅仅写入那一个新增的文件。

package-lock.json 和 york.lock

package-lock.json 和 york.lock 是为了解决扁平化的 node_modules 不确定性的问题的。保证 install 之后都产生确定的 node_modules 结构。

相关推荐
微臣愚钝2 小时前
前端【8】HTML+CSS+javascript实战项目----实现一个简单的待办事项列表 (To-Do List)
前端·javascript·css·html
lilu88888883 小时前
AI代码生成器赋能房地产:ScriptEcho如何革新VR/AR房产浏览体验
前端·人工智能·ar·vr
LCG元3 小时前
Vue.js组件开发-实现对视频预览
前端·vue.js·音视频
傻小胖3 小时前
shallowRef和shallowReactive的用法以及使用场景和ref和reactive的区别
javascript·vue.js·ecmascript
阿芯爱编程3 小时前
vue3 react区别
前端·react.js·前端框架
烛.照1034 小时前
Nginx部署的前端项目刷新404问题
运维·前端·nginx
YoloMari4 小时前
组件中的emit
前端·javascript·vue.js·微信小程序·uni-app
CaptainDrake4 小时前
力扣 Hot 100 题解 (js版)更新ing
javascript·算法·leetcode
浪浪山小白兔4 小时前
HTML5 Web Worker 的使用与实践
前端·html·html5
疯狂小料5 小时前
React 路由导航与传参详解
前端·react.js·前端框架