先记住这个答案
spawn 默认直接启动指定程序,以参数数组传参,并通过流让父进程消费标准输出和错误输出;exec 默认启动 shell 执行命令字符串,并为完成回调收集输出,受到 maxBuffer 限制。需要持续日志、大数据或细致进程控制时通常选择 spawn;需要受控 shell 管道且输出有界时可以考虑 exec。若只想直接运行一个程序并取得少量完整输出,execFile 更贴近需求。它们的异步版本都不会同步等待整个子进程结束,但仍要管理错误、退出和输出消费。
- spawn 默认不启用 shell
- exec 会解析命令字符串并收集输出
- execFile 适合直接程序加有界结果
参数数组不会自动获得 shell 的语法
使用默认 spawn 时,管道符、重定向符和通配符作为参数传给目标程序,不会自动变成 shell 管道或文件展开。如果确实需要连接两个程序,可以在父进程中连接它们的流,或明确使用受控 shell 命令。
exec 会把字符串交给 shell,字符串中的变量展开和元字符因此有含义。把用户输入拼到命令中会改变执行语义,不能用 JSON.stringify 当作跨平台 shell 转义;多数单程序调用可以直接改用参数数组。
输出消费方式影响内存与响应时机
exec 的便利在于回调拿到收集的 stdout 和 stderr,但两路输出都有缓冲上限。它虽然也暴露流,使用完成回调的输出收集仍需要预算,不能因为监听了 data 就认为收集成本已消失。
spawn 适合边产生边消费数据,例如编译日志或压缩结果。父级需要消费 stdout 和 stderr,必要时使用支持背压的流连接;若把所有 chunk 继续追加到一个无限增长的数组,内存问题仍然存在。
异步调用不代表可以忽略进程管理
两者的异步接口会返回子进程对象,主事件循环可以继续处理其他任务。启动失败要监听 error,完成结果需要结合退出码和信号,收集输出的流程还应等到流已关闭,而不是只看进程退出事件。
长任务还需要超时、取消和并发上限,尤其是在服务端请求里触发外部程序时。发送终止信号不等于所有后代进程已经结束;如果使用 shell,进程树关系会更复杂,应围绕真正的任务所有者设计回收。
容易答错的地方
- 把 spawn 说成始终无缓冲无内存成本
- 操作系统管道、Node 流以及业务代码都有缓冲。spawn 只是提供流式处理方式,是否可控取决于消费速度与累计策略;持续累加日志而不限制大小,仍会让父进程内存不断增长。
- 认为 exec 只有同步版本
- exec 本身是异步接口,execSync 才会同步等待。异步版本仍启动 shell 并管理输出,因此执行语义、输出上限和事件循环是否被同步阻塞,应作为不同问题分别回答。
面试官还会怎么问?
为什么还需要 execFile 这个接口?
它默认直接运行可执行文件并接收参数数组,同时保留完成回调取得 stdout 和 stderr 的便利。适合输出有界的单程序调用,但仍有 maxBuffer 等限制,也要防范目标程序自己的危险选项。
spawn 的 data 事件一次就是一行吗?
不是。流的 chunk 边界由传输和读取决定,一行可能拆成多个 chunk,多行也可能一起到达。需要按行处理时应使用增量分帧或 readline,并正确处理字符编码与最后一段数据。
输出很多但父进程不关心怎么办?
可以明确选择忽略输出,或把输出导向合适文件与日志系统。不要保留默认管道却不读取,否则有限容量的管道可能使子进程等待,表现为任务似乎卡住而不是自动丢弃日志。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。