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.stdinreadline模块可以更方便地处理行输入。

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进程可以接收和处理操作系统信号,下面例子演示了如何接收和处理SIGINTSIGTERM

// 处理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
作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。