NodeJS - 按数组中每个元素之间的超时顺序循环遍历数组

NodeJS - Looping through Array Sequentially with Timeout between each Element in Array

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

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 return 的承诺,这样你就知道什么时候完成了;你不能依赖超时(如果任务需要超过三秒怎么办?)并且由于大多数这些命令将完成 much 比这快得多,超时会不必要地阻止事情。

这是execCommand return一个承诺:

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...
}

如果不这样做,请将其包装在 async IIFE 中:

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

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

目前您不能保证调用下一个命令时上一个命令将完成。您在 3000 毫秒后自动调用下一个,但上一个可能需要比预期更长的时间并且尚未结束。

您应该添加一种机制来等待每个命令,然后启动下一个命令。下面是如何使用 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);
    }
})();