NodeJS -在数组中的每个元素之间依次循环并超时



我在数组中有一个命令列表,我需要按顺序运行:

const commands = [
`git clone https://github.com/EliLillyCo/${repo}.git`,
`cd ${repo}`, `git checkout -b ${branch}`,
'cp ../codeql-analysis.yml .github/workflows/',
'git add .github/workflows/codeql-analysis.yml',
`git push --set-upstream origin ${branch}`,
'cd ../',
`rm -r  ${repo}`,
];

他们按顺序运行,因为命令依赖于前一个正在运行的命令。

此外,每个命令在运行下一个命令之前需要有3秒的等待时间,因为有时命令需要时间,特别是命令1和命令5.

我正在使用一个标准的for循环,然后使用setTimeout()调用一个函数来运行命令,如下:


const a = require('debug')('worker:sucess');
const b = require('debug')('worker:error');
const { exec } = require('child_process');
function execCommand(command) {
exec(command, (error, stdout, stderr) => {
if (error) {
b(`exec error: ${error}`);
return;
}
a(`stdout: ${stdout}`);
b(`stderr: ${stderr}`);
});
}
const commands = [
`git clone https://github.com/EliLillyCo/${repo}.git`,
`cd ${repo}`, `git checkout -b ${branch}`,
'cp ../codeql-analysis.yml .github/workflows/',
'git add .github/workflows/codeql-analysis.yml',
`git push --set-upstream origin ${branch}`,
'cd ../',
`rm -r  ${repo}`,
];
for (let i = 0; i < commands.length; i++) {
setTimeout(execCommand(commands[i]), 3000);
}

但是setTimeout()有问题,因为它返回这个:

worker:error TypeError [ERR_INVALID_CALLBACK]: Callback must be a function. Received undefined

什么是最好的方法来解决问题的循环通过一个数组顺序,同时使用超时?

我会让execCommand返回一个承诺,所以你知道什么时候完成;您不能依赖于超时(如果任务需要超过三秒呢?),并且由于大多数命令将以比这快得多的速度完成,因此超时会不必要地耽搁事情。

execCommand返回一个承诺:

function execCommand(command) {
return new Promise((resolve, reject) => {
exec(command, (error, stdout, stderr) => {
if (error) {
b(`exec error: ${error}`);
reject(error);
return;
}
a(`stdout: ${stdout}`);
b(`stderr: ${stderr}`);
resolve();
});
});
}

如果你有顶级await可用(现代Node.js和ESM模块):

// If you have top-level `await` available
try {
for (const commmand of commands) {
await execCommand(command);
}
} catch (error) {
// ...report/handle error...
}

如果你没有,用asyncife:

(async () => {
for (const commmand of commands) {
await execCommand(command);
}
})().catch(error => {
// ...report/handle error...
});

或者,如果你想把stdout/stderr的执行分开,你可以直接在exec上使用util.promisify,但是把它们放在一起是对你所拥有的最小的改变,所以这就是我坚持的。

目前您不能保证在调用下一个命令时将完成前一个命令。你在3000ms后自动调用下一个,但是前一个可能比预期的要长,而且还没有结束。

您应该添加一个机制来等待每个命令,然后启动下一个命令。下面是如何使用async/await:

const util = require('util');
const exec = util.promisify(require('child_process').exec);
const commands = [ ... ];
const execCommand = async (command) => {
try {
await exec(command)
} catch (error) {
b(`exec error: ${error}`);
return;
}
a(`stdout: ${stdout}`);
b(`stderr: ${stderr}`);
}
(async () => {
for (let command of commands) {
await execCommand(command);
}
})();

最新更新