process模块
process是NodeJS中的一个全局对象,它提供了当前NodeJS进程的信息和控制能力。通过process对象,我们可以获取环境变量、命令行参数、进程信息,以及控制进程的输入输出和退出行为等。
进程信息
process对象提供了许多属性用于获取当前进程的相关信息。
// 进程ID
console.log(process.pid);
// 父进程ID
console.log(process.ppid);
// NodeJS版本
console.log(process.version);
// NodeJS及其依赖库的版本信息
console.log(process.versions);
// 操作系统平台
console.log(process.platform);
// CPU架构
console.log(process.arch);
// 当前工作目录
console.log(process.cwd());
// NodeJS可执行文件路径
console.log(process.execPath);
// 进程启动时间(返回Unix时间戳,单位秒)
console.log(process.uptime());
环境变量
process.env是一个包含当前进程环境变量的对象,我们可以读取和修改环境变量。
// 读取环境变量
console.log(process.env.PATH);
console.log(process.env.NODE_ENV);
// 设置环境变量(仅在当前进程中生效)
process.env.MY_VAR = "hello";
console.log(process.env.MY_VAR);
// 删除环境变量
delete process.env.MY_VAR;
在实际开发中,我们通常会通过环境变量来区分开发环境和生产环境,这一操作十分常用。
if (process.env.NODE_ENV === "production") {
console.log("生产环境");
} else {
console.log("开发环境");
}
命令行参数
process.argv是一个数组,包含了启动NodeJS进程时的命令行参数。
// 假设执行命令:node app.js --name=tom --age=18
console.log(process.argv);
// 输出:
// [
// '/usr/local/bin/node', // NodeJS可执行文件路径
// '/path/to/app.js', // 脚本文件路径
// '--name=tom', // 自定义参数
// '--age=18' // 自定义参数
// ]
// 获取用户传入的参数(跳过前两个)
const args = process.argv.slice(2);
console.log(args); // ['--name=tom', '--age=18']
process.execArgv则用于获取传递给NodeJS本身的命令行选项。
// 假设执行命令:node --inspect app.js
console.log(process.execArgv); // ['--inspect']
标准输入输出
process对象提供了三个标准流对象用于进程的输入输出操作。
标准输出
process.stdout是标准输出流,我们可以通过它向控制台输出内容。
// 输出内容(不自动换行)
process.stdout.write("Hello, ");
process.stdout.write("World!\n");
// 实际上,console.log就是对process.stdout.write的封装
console.log("Hello, World!");
标准错误输出
process.stderr是标准错误输出流,用于输出错误信息。
process.stderr.write("Error: Something went wrong!\n");
// console.error是对process.stderr.write的封装
console.error("Error: Something went wrong!");
标准输入
process.stdin是标准输入流,我们可以通过它读取用户输入。
// 设置编码
process.stdin.setEncoding("utf8");
console.log("请输入内容:");
// 监听data事件读取输入
process.stdin.on("data", (chunk) => {
const input = chunk.trim();
console.log(`你输入了:${input}`);
process.exit(0);
});
不过我们一般不会这样直接操作process.stdin,readline模块可以更方便地处理行输入。
import * as readline from "node:readline/promises";
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const answer = await rl.question("请输入你的名字:");
console.log(`你好,${answer}!`);
rl.close();
进程退出
我们可以通过process.exit()方法主动退出进程。
// 正常退出(退出码为0)
process.exit(0);
// 异常退出(退出码为非0值)
process.exit(1);
我们可以通过监听exit事件在进程退出前执行一些清理操作。
process.on("exit", (code) => {
console.log(`进程即将退出,退出码:${code}`);
// 注意:此处只能执行同步操作
});
beforeExit事件在NodeJS清空事件循环且没有其他工作要安排时触发。
process.on("beforeExit", (code) => {
console.log(`beforeExit事件触发,退出码:${code}`);
// 此处可以执行异步操作,但会阻止进程退出
});
信号处理
NodeJS进程可以接收和处理操作系统信号,下面例子演示了如何接收和处理SIGINT和SIGTERM。
// 处理SIGINT信号(通常是Ctrl+C)
process.on("SIGINT", () => {
console.log("收到SIGINT信号,准备退出...");
// 执行清理操作
process.exit(0);
});
// 处理SIGTERM信号(通常是kill命令)
process.on("SIGTERM", () => {
console.log("收到SIGTERM信号,准备退出...");
process.exit(0);
});
console.log("进程运行中,按Ctrl+C退出");
// 保持进程运行
setInterval(() => {}, 1000);
内存使用
process.memoryUsage()方法返回当前进程的内存使用情况。
const memUsage = process.memoryUsage();
console.log(memUsage);
// 格式化输出
const formatBytes = (bytes) => {
return (bytes / 1024 / 1024).toFixed(2) + " MB";
};
console.log(`堆内存使用:${formatBytes(memUsage.heapUsed)}`);
console.log(`堆内存总量:${formatBytes(memUsage.heapTotal)}`);
CPU使用
process.cpuUsage()方法返回当前进程的CPU使用时间。
const startUsage = process.cpuUsage();
// 假设这里执行一些CPU密集型操作
let sum = 0;
for (let i = 0; i < 1e8; i++) {
sum += i;
}
const endUsage = process.cpuUsage(startUsage);
console.log(endUsage);
异常处理
process对象可以监听未捕获的异常和未处理的Promise拒绝。
未捕获异常
process.on("uncaughtException", (err, origin) => {
console.error("未捕获的异常:", err);
console.error("异常来源:", origin);
process.exit(1);
});
// 触发未捕获异常
throw new Error("这是一个未捕获的异常");
未处理的Promise拒绝
process.on("unhandledRejection", (reason, promise) => {
console.error("未处理的Promise拒绝:", reason);
});
// 触发未处理的Promise拒绝
Promise.reject(new Error("这是一个未处理的Promise拒绝"));
注意:虽然可以通过这些事件捕获异常,但在生产环境中,最佳实践仍然是在代码中正确处理所有可能的异常情况。
nextTick
process.nextTick()方法用于将回调函数添加到"下一个时钟周期"队列中,它会在当前操作完成后、事件循环继续之前执行。
console.log("开始");
process.nextTick(() => {
console.log("nextTick回调");
});
Promise.resolve().then(() => {
console.log("Promise回调");
});
console.log("结束");
输出顺序如下。
开始
结束
nextTick回调
Promise回调
process.nextTick()使用时要注意避免误写递归。
function dangerous() {
process.nextTick(dangerous);
}
process.nextTick()的执行优先级高于Promise的微任务,这在某些需要确保回调在异步操作之前执行的场景中很有用。
function asyncOperation(callback) {
// 确保回调总是异步执行
process.nextTick(() => {
callback(null, "result");
});
}
asyncOperation((err, result) => {
console.log(result);
});
console.log("asyncOperation调用完成");
输出顺序如下。
asyncOperation调用完成
result