之前做的一个cli工具,要求可以将项目打包编译后上传至服务器,一直使用效果很好。今天同事找到我,告诉我项目必须上传两次才能生效,我就很奇怪为什么,经过短暂定位,发现第一次上传的是之前的编译文件。这就引起我的好奇了,明明代码都没有动过怎么会出现这样的情况。遂将同事的项目和之前使用的项目进行编译上传比对,发现同事的项目编译并没有完成,子进程中途异常中断,所以上传的文件是之前编译的旧文件。下图是同事的项目的输出情况:

此时看似项目已经编译完成,事实上并没有,经过一段时间后编译文件才真正覆盖原文件。下图是正确编译后的情况:

输出了编译后的所有文件。明明都是一样的编译流程,为何却出现了不同的结果?经过研究发现是child_process.exec方法导致子进程异常中断引起的,父进程继续执行上传代码,最终导致上传到服务的文件与预期不同。查询node API 发现12版本前的nodeJS,child_process.exec的maxBuffer参数默认为200kb,项目比较复杂的时候,子进程输出流很容易就超出这个最大限制,然后子进程将终止并截断任何输出,这就是问题所在,不过node版本12之后已经将默认值提升为1M,可以适用大部分的情况。

解决方案一:
升级node版本为12+(仅适用1M以下输出流的情况)
解决方案二:
执行exec方法时,参数中设置一个比较大的maxBuffer值(仅适用最大输出流比较明确的情况,而且exec不是用来返回结果数据的,是用来返回结果状态,尽可能不将maxBuffer设置的特别大)
exec(cmd,{maxBuffer: 1024*1024}) // cmd为执行命令
解决方案三:
使用child_process.spawn代替exec方法,spawn方法是没有最大输出限制的,所以可以完美适配需要获取结果数据的场景。spawn的具体使用方法可以去看官方API (https://nodejs.org/api/child_process.html#child_processspawncommand-args-options)
function asyncExec () {
var ls = spawn('cmd',['/c', 'npm', 'run', 'build'])
return new Promise((resolve, reject) => {
ls.stdout.on('data', (data) => {
log(data.toString('utf8'))
})
ls.stderr.on('data', (data) => {
logRed(data.toString('utf8'))
})
ls.on('error', reject)
ls.on('close', resolve)
})
}
最终结果:

遇到难题看文档!看文档!看文档!
阅读原文