node:process
这个模块直接暴露在了全局作用于当中,可以直接访问 process 对象
进程的退出事件
exit/beforeExit
有多种方式来监听进程的退出事件,比如常规的 exit/beforeExit 事件,二者有一些区别
监听进程的退出事件,对于导致显式终止的异常(比如手动调用 process.exit()),beforeExit 不会被触发
process.on('exit', (code) => {
console.log('exit', code); // => exit 0
})
process.on('beforeExit', (code) => {
console.log('beforeExit', code); // => beforeExit 0
})
// process ends naturallyprocess.on('exit', (code) => {
console.log('exit', code);
})
process.on('beforeExit', (code) => {
console.log('beforeExit', code); // => beforeExit 1
})
process.exit(1)TIP
process 也对象是 EventEmitter 的实例,并且内置了一系列的事件
SIGTERM/SIGINT/SIGQUIT
它们都属于 signal event,也是经常用于监听进程退出场景
譬如在生成一个子进程的时候,对子进程通知的 signal event,结束进程
import { fork } from 'child_process'
let childProc
const onShuntDown = () => {
if(childProc) {
childProc.kill()
}
}
childProc = fork('./child.js')
process.on('exit', onShutdown)
process.on('SIGTERM', onShutdown) // kill pid
process.on('SIGINT', onShutdown) // CMD(ctrl) + C
process.on('SIGQUIT', onShutdown) // CMD(ctrl) + \保留进程
有时,我们不想让进程运行完了代码后就结束,可以通过 process.stdin.resume 方法,仍然进程保持活跃
// index.ts
process.stdin.resume()
function handle(signal: string) {
console.log(`Received ${signal}`);
}
console.log(process.pid)
// Interrupt from keyboard
process.on('SIGINT', handle);
// Termination signal
process.on('SIGTERM', handle);
// Quit from keyboard
process.on('SIGQUIT', handle);$ tsx index.ts
86489
^CReceived SIGINT # press Ctrl-C
$ kill 86871 # run in another terminal
Received SIGTERM
^\Received SIGQUIT # press Ctrl-\终止进程
有时候想在代码的某一处终止进程,可以直接调用 process.exit(),默认的 code 为 0 如果没有显式设置 proess.exitCode 的值
process.on('exit', (code) => {
console.log('exit', code); // => 0
})
process.exit()或者显示式设置 proess.exitCode
process.on('exit', (code) => {
console.log('exit', code); // => 2
})
process.exitCode = 2
process.exit()正常情况下无需调用,如果事件循环中没有其他待处理的工作,进程运行完毕后会自动结束
TIP
虽然 exitCode 我们可以自定义,但是也有一定的规范遵循,参考 process#exit-codes
全局异常捕获
通过 unhandledRejection/uncaughtException 两个事件,对当前进程的异常进行捕获。可以监听到那些没有进行异常处理的局部代码
捕获 Promise 的reject
通过 unhandledRejection 来捕获未用 catch 处理的 reject
process.on('unhandledRejection', err => console.error('[uncaughtException]', err))
// => [unhandledRejection] end
Promise.reject('end')捕获其他异常
uncaughtException 事件捕获的范围则要广的多,接管了所有的异常,例如:
process.on('uncaughtException', err => console.error('[uncaughtException]', err))
// => [uncaughtException] ...
// 使用未定义的变量
sdf
// 抛出一个错误
throw new Error()TIP
如有必要,二者都可以作为常态代码一起放在进程中,提高代码健壮性
arch
process.arch 用于获取运行当前进程的 CPU 架构
process.arch //=> arm64和 os.arch() 的区别在于:
- 如果要获取当前进程的
CPU架构,使用process.arch - 如果要无视运行的进程,获取操作系统的
CPU架构,使用os.arch() os.arch()在某些平台上可能不可用,这时可以用process.arch来回退获取架构信息
参数
通过下面四个对象可以访问到命令行的不同参数
process.argvprocess.execPathprocess.argv0process.execArgv
详情如下:
process.argv 属性返回一个数组,其中包含启动进程时传递的命令行参数。第一个元素是 process.execPath。
// index.js
console.log(
process.execPath,
process.argv
)$ node index.js
/path/to/.nvm/versions/node/v18.16.1/bin/node
[
'/path/to/.nvm/versions/node/v18.16.1/bin/node',
'/path/to/project/index.js'
]上面可以看到,通过 argv[0]/execPath 我们可以得到 node 的文件路径,但这可能不是我们想要的,我们仅仅想要第一个参数,即 node 字符串本身,而不是它的原始路径
如果需要访问 argv[0] 的原始值,可以访问 process.argv0。
// index.js
console.log(
process.argv0,
process.argv
)$ node index.js
node
[
'/path/to/.nvm/versions/node/v18.16.1/bin/node',
'/path/to/project/index.js'
]关于参数,还有一个变量 process.execArgv
process.execArgv 属性返回 Node.js 进程启动时传递的 Node.js 特定命令行选项集。这些选项不会出现在 process.argv 属性返回的数组中,也不包含 Node.js 可执行文件、脚本名称或脚本名称后面的任何选项。这些选项对于生成具有与父进程相同的执行环境的子进程很有用。
举个例子
// index.js
console.log(
process.execArgv,
process.argv
)$ node --harmony index.js --version
[ '--harmony' ]
[
'/path/to/.nvm/versions/node/v18.16.1/bin/node',
'/path/to/project/index.js',
'--version'
]还有一个有意思例子,之前我们提过 tsx,我们来看使用它的时候,传递的参数是什么:
// index.js
console.log(
process.execPath,
process.argv0,
process.execArgv,
process.argv
)$ tsx index.js
/path/to/.nvm/versions/node/v18.16.1/bin/node # execPath
/path/to/.nvm/versions/node/v18.16.1/bin/node # argv0
[ # execArgv
'--require',
'/path/to/.nvm/versions/node/v16.20.1/lib/node_modules/tsx/dist/preflight.cjs',
'--loader',
'file:///path/to/.nvm/versions/node/v16.20.1/lib/node_modules/tsx/dist/loader.js'
]
[ # argv
'/path/to/.nvm/versions/node/v18.16.1/bin/node',
'/path/to/project/index.js',
]上面可以看到,tsx 本质上也是在用 node 执行文件,只不过是加了一个 loader 从而实现 ts 到 js 的编译,进而让 node 执行
这等价于:
$ node \
--require /path/to/.nvm/versions/node/v16.20.1/lib/node_modules/tsx/dist/preflight.cjs \
--loader file:///path/to/.nvm/versions/node/v16.20.1/lib/node_modules/tsx/dist/loader.js \
/path/to/project/index.jscwd
返回当前进程的工作目录
// /path/to/project/index.js
console.log(process.cwd())$ node index.js
/path/to/project之前在讲 path 模块的 resolve 第二点中,提过path.resolve 会从当前目录(cwd)开始计算 ,所以 path.resolve 也是返回当前进程工作目录
实际上 process.cwd() 和 path.resolve() 的结果是一样的
改变工作目录
上面说到 cwd() 可以得到当前进程的工作目录,我们还可以在进程运行途中通过 process.chdir() 改变它
console.log(`Start: ${process.cwd()}`)
try { // 有可能会失败
process.chdir('/tmp')
console.log(`End: ${process.cwd()}`)
} catch (e) {
console.log(e)
}pid/ppid
获得当前进程/父进程的唯一 id
console.log(
process.pid, //=> 43335
process.ppid, //=> 43334
)进程运行时间
process.uptime() 返回当前进程的运行时间
setInterval(() => {
console.log(
process.uptime(),
)
}, 1000)
/*
1.051779208
2.052671375
3.052353708
...
*/version(s)
process.version 返回当前进程使用的 Node.js 版本
console.log(
process.version //=> v18.16.1
)process.versions 属性返回一个对象,列出 Node.js 的版本字符串及其依赖项
console.log(process.versions)
/*
{
node: '18.16.1',
acorn: '8.8.2',
ada: '1.0.4',
ares: '1.19.1',
brotli: '1.0.9',
cldr: '42.0',
icu: '72.1',
llhttp: '6.0.11',
modules: '108',
napi: '8',
nghttp2: '1.52.0',
nghttp3: '0.7.0',
ngtcp2: '0.8.1',
openssl: '3.0.9+quic',
simdutf: '3.2.2',
tz: '2022g',
undici: '5.21.0',
unicode: '15.0',
uv: '1.44.2',
uvwasi: '0.0.15',
v8: '10.2.154.26-node.26',
zlib: '1.2.13'
}
*/platform
process.platform 属性返回一个字符串,标识编译 Node.js 二进制文件的操作系统平台
console.log(process.platform) //=> darwin和 os.platform() 的区别依旧是强调当前进程还是操作系统