Node.js CLI迁移脚本提前退出未执行migrate方法如何解决
问题根因
- 你代码里所有IO操作(文件读取、执行shell命令)都是异步非阻塞的,触发任务后不会等结果返回就会继续执行后续代码
- 你在
fs.readdir的回调里遍历完文件立刻调用了process.exit(),此时所有migrate触发的shell任务还没执行完,脚本就直接退出了 - 原
migrate函数内部也错误写了process.exit(),只要任意一个任务执行完就会直接退出脚本,导致后续任务无法执行 Array.prototype.forEach不支持等待异步任务执行完成,即使你给migrate加了async,forEach也不会等所有异步任务结束
修复方案
我们统一用async/await改写异步逻辑,保证所有迁移任务执行完成后再退出脚本:
第一步:改写migrate函数
首先用util.promisify把回调式的exec转成Promise版本,去掉内部的process.exit(),把执行结果返回给上层统一处理:
const { promisify } = require('util'); const exec = promisify(require('child_process').exec); const migrate = async (space, env, migration) => { try { const { stdout, stderr } = await exec(`migrate ${migration} "${space}" ${env}`); if (stderr) { console.log(`stderr: ${stderr}`); throw new Error(stderr); } success(stdout); return stdout; } catch (error) { // 原有错误处理switch逻辑放这里 throw error; } };
第二步:改写migrateAll函数
统一用fs.promises的异步API,替换回调写法,等待所有迁移任务执行完成后再统一处理退出:
const migrateAll = async (app, env, source) => { // 读取配置文件 const contents = await fs.promises.readFile(config, "utf8"); this.config = JSON.parse(contents); const spaceId = this.config.applications[app].space_id; // 读取目录文件 const files = await fs.promises.readdir(source, "utf8"); console.log(`共找到${files.length}个待迁移文件`); // 生成所有迁移任务的Promise数组 const migrateTasks = files.map((file) => { console.log(chalk.yellow(`Migrating "${file}" to the "${env}" environment for "${app}"`)) return migrate(file, env, app); }); // 等待所有任务执行完成(如果需要串行执行可以用for of循环挨个await) await Promise.all(migrateTasks); console.log(chalk.green('所有迁移任务执行完成')); process.exit(0); };
第三步:改写上层调用逻辑,等待异步任务执行
因为migrateAll现在是异步函数,返回Promise,所以上层调用也要加await:
const parseFlags = async () => { process.argv.splice(2).forEach((arg) => { let parts = arg.split("="); switch (parts[0]) { // 原有参数解析逻辑保留 } }); if (all) { // 等待迁移任务执行完成 await migrateAll(app, env, source); } return { app, env, source }; }; const run = async () => { try { intro(); checkSettings(); const { app, env, source } = await parseFlags(); // 原有非迁移场景的后续逻辑保留 } catch (err) { complain(err); process.exit(1); } }; run();
额外注意
如果待迁移文件数量很多,直接用Promise.all会同时触发大量shell进程,可能导致系统资源不足,你可以改成串行执行的写法:
// 替换原来的Promise.all部分 for (const file of files) { console.log(chalk.yellow(`Migrating "${file}" to the "${env}" environment for "${app}"`)) await migrate(file, env, app); }
内容的提问来源于stack exchange,提问作者phatskat
相关产品推荐
相关产品推荐

