child_process.fork() 是 Node.js/Electron 中专门用于创建 Node.js 子进程的方法,它基于 spawn() 但针对 Node.js 进程进行了优化,内置了进程间通信(IPC)通道。

一、fork() 的核心功能

  1. 专用 Node.js 进程:只能用于启动 Node.js 脚本(.js 文件)
  2. 内置 IPC 通道:自动建立父子进程间的通信通道
  3. 高效数据传输:支持发送 JavaScript 对象(序列化/反序列化)
  4. 内存隔离:子进程有独立的内存空间和 V8 实例
  5. 模块继承:子进程继承父进程的模块系统

二、方法签名

const { fork } = require('child_process');
const child = fork(modulePath[, args][, options]);

三、参数详解

1. modulePath (必需)

要运行的 Node.js 模块路径(相对于当前工作目录)

2. args (可选)

传递给子进程的参数数组

3. options (可选)

配置对象,特殊选项(除常规 spawn 选项外):

选项 类型 默认值 描述
execPath string process.execPath 用于创建子进程的 Node.js 可执行文件路径
execArgv Array process.execArgv 传递给 Node.js 可执行文件的参数
silent boolean false 是否不继承父进程的 stdio
serialization string 'json' 进程间通信的序列化方法 ('json' 或 'advanced')

四、使用注意事项

1. 安全性考虑

  • 路径安全​:永远使用 path.join() 构造绝对路径
// 错误做法
fork(`./workers/${userInput}.js`);

// 正确做法
const safePath = path.join(__dirname, 'workers', 'predefined-worker.js');
fork(safePath);
  • IPC 过滤​:验证所有接收的消息
child.on('message', (msg) => {
  if (msg.type && ALLOWED_TYPES.includes(msg.type)) {
    // 处理消息
  }
});

2. 资源管理

  • 内存泄漏​:定期重启长时间运行的子进程
  • 进程限制​:避免无限制创建子进程
const MAX_WORKERS = require('os').cpus().length;
const activeWorkers = new Set();

function createWorker() {
  if (activeWorkers.size >= MAX_WORKERS) {
    throw new Error('Worker limit reached');
  }

  const worker = fork(path.join(__dirname, 'worker.js'));
  activeWorkers.add(worker);
  worker.on('exit', () => activeWorkers.delete(worker));
  return worker;
}

3. 错误处理

  • 全面捕获​:处理所有可能的错误事件
child.on('error', (err) => {
  console.error('Process error:', err);
});

child.on('exit', (code, signal) => {
  if (code !== 0) {
    console.error(`Process exited abnormally with code ${code}`);}
});

process.on('uncaughtException', (err) => {
  console.error('Uncaught exception in worker:', err);
});

五、完整用法示例

前置条件

  • 需要在module.json5中添加JIT权限,否则会出现fork时crash现象

    image

基础示例:简单进程创建

主进程 (main.js):

const { app, BrowserWindow, ipcMain, Tray, nativeImage } = require('electron');
const { fork } = require('child_process');
const path = require('path');

let mainWindow, tray;

function createWindow() {
    tray = new Tray(nativeImage.createFromPath(path.join(__dirname, 'electron_white.png')));
    mainWindow = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
        }
    });
    console.log('electron-demo start');
    mainWindow.loadFile('index.html');
}

app.whenReady().then(() => {
    createWindow();

    app.on('activate',() => {
        if(BrowserWindow.getAllWindow().length === 0) createWindow();
    });

    app.on('window-all-closed', () => {
        if (process.platform !== 'darwin') app.quit();
    });
});

// fork
console.log('electron-demo 即将开始fork!' + __dirname);
const child = fork(path.join(__dirname,'child.js'));

child.on('message',(message) => {
    console.log('electron-demo 主进程收到消息',message);
    console.log('child.connected: ', child.connected);
});

child.send({helo:'electron-demo from main process'});

// 在一段时间后关闭IPC通道
setTimeout(() => {
    console.log('electron-demo Parent: disconnecting IPC...');
    child.disconnect(); // 显式关闭IPC通道
}, 3000);

// 监听'disconnect'事件
child.on('disconnect', () => {
    console.log('electron-demo Parent: IPC disconnected.');
    console.log('electron-demo child.connected: ', child.connected);
    console.log('electron-demo child.exitCode: ', child.exitCode)
});

child.on('exit', (code) => {
    console.log(`electron-demo Child process exited with code ${code}`)
})

子进程 (child.js):

process.on('message',(message) => {
    console.log('子进程收到消息:',message);

    process.send({hello:'electron-demo from child process',received:message});

    process.send({status:'electron-demo child process started'});
})

运行效果

image

image

实际应用:CPU 密集型任务

主进程:

const { app, BrowserWindow, ipcMain, Tray, nativeImage } = require('electron');
const { fork } = require('child_process');
const path = require('path');

let mainWindow, tray;

function createWindow() {
    tray = new Tray(nativeImage.createFromPath(path.join(__dirname, 'electron_white.png')));
    mainWindow = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
        }
    });
    console.log('electron-demo start');
    mainWindow.loadFile('index.html');
}

app.whenReady().then(() => {
    createWindow();

    app.on('activate',() => {
        if(BrowserWindow.getAllWindow().length === 0) createWindow();
    });

    app.on('window-all-closed', () => {
        if (process.platform !== 'darwin') app.quit();
    });
});

// fork
console.log('electron-demo 即将开始fork!' + __dirname);
const child = fork(path.join(__dirname,'fibonacci-worker.js'));

// 发送计算任务
child.send({ number: 40 });

child.on('message', (result) => {
    console.log(`Fibonacci result: ${result.value}`);
    child.kill(); // 任务完成后终止子进程
});

child.on('error', (err) => {
    console.error('Worker error:', err);
});

fibonacci-worker.js:

function fibonacci(n) {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
}

process.on('message', ({ number }) => {
  const result = fibonacci(number);
  process.send({ value: result });
});

运行效果

image

image

高级示例:进程池管理

process-pool.js:

const { fork } = require('child_process');
const os = require('os');

class ProcessPool {
  constructor(modulePath, size = os.cpus().length) {
    this.pool = [];
    this.queue = [];
    this.maxSize = size;
    this.modulePath = modulePath;

    for (let i = 0; i < size; i++) {
      this.createWorker();
    }
  }

  createWorker() {
    const worker = fork(this.modulePath);
    worker.busy = false;

    worker.on('message', (result) => {
      worker.busy = false;
      const callback = this.queue.shift();
      if (callback) {
        this.runTask(callback.task, callback.resolve, callback.reject);
      }
    });

    this.pool.push(worker);
    return worker;
  }

  runTask(task, resolve, reject) {
    const availableWorker = this.pool.find(w => !w.busy);

    if (availableWorker) {
      availableWorker.busy = true;
      availableWorker.send(task);

      availableWorker.once('message', resolve);
      availableWorker.once('error', reject);
    } else if (this.pool.length < this.maxSize) {
      const newWorker = this.createWorker();
      this.runTask(task, resolve, reject);
    } else {
      this.queue.push({ task, resolve, reject });
    }
  }

  execute(task) {
    return new Promise((resolve, reject) => {
      this.runTask(task, resolve, reject);
    });
  }
}

module.exports = ProcessPool;

六、高级配置技巧

1. 定制 Node.js 执行环境

const child = fork(path.join(__dirname, 'worker.js'), [], {
  execPath: '/usr/local/bin/node', // 指定 Node.js 路径
  execArgv: ['--inspect=9229']     // 启用子进程调试
});

2. 静默模式(不继承 stdio)

const child = fork(path.join(__dirname, 'worker.js'), [], {
  silent: true  // 子进程不会继承父进程的 stdio
});

// 手动处理输出
child.stdout.on('data', (data) => {
  console.log('Worker stdout:', data.toString());
});

child.stderr.on('data', (data) => {
  console.error('Worker stderr:', data.toString());
});

3. 高级序列化(Node.js v13.2.0+)

const child = fork(path.join(__dirname, 'worker.js'), [], {
  serialization: 'advanced'  // 支持更多 JS 类型
});

// 可以发送特殊对象
child.send({
  date: new Date(),
  set: new Set([1, 2, 3]),
  map: new Map([['key', 'value']])
});

七、与 Electron 集成的特殊考虑

  1. 渲染进程中使用
  • renderer.js
// 渲染进程 (renderer.js)
document.getElementById('start-btn').addEventListener('click', async () => {
    const input = document.getElementById('input-data').value
    const resultDiv = document.getElementById('result')

    resultDiv.textContent = 'Processing...'

    try {
        // 调用主进程
        await window.electronAPI.startTask(input)

        // 设置回复监听
        window.electronAPI.onWorkerReply((result) => {
            resultDiv.textContent = `Result: ${result}`
        })
    } catch (error) {
        resultDiv.textContent = `Error: ${error.message}`
    }
})
  • main.js
const { app, BrowserWindow, ipcMain, Tray, nativeImage } = require('electron');
   const { fork } = require('child_process');
   const path = require('path');

   let mainWindow, tray, workerProcess;

   function createWindow() {
       tray = new Tray(nativeImage.createFromPath(path.join(__dirname, 'electron_white.png')));
       mainWindow = new BrowserWindow({
           width: 800,
           height: 600,
           webPreferences: {
               nodeIntegration: false,
               contextIsolation: true,
               preload: path.join(__dirname, 'preload.js')
           }
       });
       console.log('electron-demo start');
       mainWindow.loadFile('index.html');
   }

   app.whenReady().then(() => {
       createWindow();

       // 创建 worker 进程
       workerProcess = fork(path.join(__dirname, 'worker.js'))
       // 监听来自 worker 的消息
       process.on('message', (message) => {
           console.log('Main process received from worker:', message)
           mainWindow.webContents.send('worker-reply', message)
       })

       console.log('---------------------> register start-task')

       // 监听渲染进程的请求
       ipcMain.handle('start-task', (event, data) => {
           console.log('Main process received from renderer:', data)
           workerProcess.send({
               type: 'compute',
               data: data
           })
       })

       // 处理worker上报消息
       workerProcess.on('message',(message) => {
           console.log('electron-demo 主进程收到消息', message.result);
           // 将数据发送到渲染进程
           mainWindow.webContents.send('worker-reply', message.result);
       })

       // 处理子进程退出
       workerProcess.on('exit', (code) => {
           console.log(`Worker process exited with code ${code}`)
       })

       app.on('activate',() => {
           if(BrowserWindow.getAllWindow().length === 0) createWindow();
       });

       app.on('window-all-closed', () => {
           if (process.platform !== 'darwin') app.quit();
       });
   });
  • preload.js
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('electronAPI', {
    startTask: (data) => ipcRenderer.invoke('start-task', data),
    onWorkerReply: (callback) => {
        ipcRenderer.on('worker-reply', (_event, value) => callback(value))
    }
})
  • worker.js
process.on('message', (message) => {
    console.log('Worker received task:', message.data)
    if (message.type === 'compute') {
        console.log('Worker compute:', message.data)

        // 模拟耗时计算
        const start = Date.now()
        let result = heavyComputation(message.data)
        const duration = Date.now() - start

        // 发送结果回主进程
        process.send({
            input: message.data,
            result: result,
            duration: duration
        })
    }
})

function heavyComputation(input) {
    // 模拟 CPU 密集型任务
    let result = 0
    for (let i = 0; i < 10; i++) {
        result += Math.sqrt(i) * Math.sin(i) * input.length
    }
    return result
}

  • index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Electron Fork Example</title>
    <style>
        body { font-family: Arial; padding: 20px; }
        input { padding: 8px; width: 200px; }
        button { padding: 8px 16px; margin-left: 10px; }
        #result { margin-top: 20px; padding: 10px; background: #f0f0f0; }
    </style>
</head>
<body>
<h1>Electron Fork Example</h1>
<div>
    <input type="text" id="input-data" placeholder="Enter some data">
    <button id="start-btn">Start Task</button>
</div>
<div id="result">Ready...</div>
<script src="renderer.js"></script>
</body>
</html>
  • 运行效果展示

    image

    image

  1. 开发与生产环境路径处理
const { app } = require('electron');
const path = require('path');

function getWorkerPath() {
  if (app.isPackaged) {
    return path.join(process.resourcesPath, 'app.asar.unpacked', 'workers', 'worker.js');
  }
  return path.join(__dirname, 'workers', 'worker.js');
}

const worker = fork(getWorkerPath());
  1. 安全最佳实践
// 在主进程中创建白名单
const ALLOWED_WORKERS = {
  'image-processor': './workers/image-processor.js',
  'data-cruncher': './workers/data-cruncher.js'
};

ipcMain.handle('start-worker', (event, { workerName, task }) => {
  if (!ALLOWED_WORKERS[workerName]) {
    throw new Error('Unauthorized worker request');
  }

  return new Promise((resolve, reject) => {
    const worker = fork(ALLOWED_WORKERS[workerName]);
    worker.send(task);
    worker.on('message', resolve);
    worker.on('error', reject);
  });
});

八、常见问题解决方案

  1. 消息丢失问题

    • 确保在发送消息前子进程已准备好
    • 使用 child.on('message', handler) 前先设置
  2. 进程不退出

    // 确保关闭所有 IPC 通道
    child.disconnect();
    // 必要时强制终止
    child.kill();
    
  3. 内存泄漏

    • 定期重启长时间运行的 worker
    • 使用 child.unref() 让子进程独立运行
  4. 调试技巧

    const child = fork(path.join(__dirname, 'worker.js'), [], {
      execArgv: ['--inspect-brk=9229'] // 暂停等待调试器连接
    });
    

通过以上详细介绍,您应该能够全面掌握 Electron 中 child_process.fork() 的使用方法,包括其核心功能、各种用法示例、测试验证方法以及在实际 Electron 项目中的应用技巧。


返回子进程目录 | 返回文档首页