【问题标题】:NodeJS - Looping through Array Sequentially with Timeout between each Element in ArrayNodeJS - 在数组中的每个元素之间按顺序循环遍历数组
【发布时间】:2021-01-27 10:39:26
【问题描述】:

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

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

在使用超时的同时,解决顺序循环数组问题的最佳方法是什么?

【问题讨论】:

  • 正确的语法是setTimeout( execCommand, 3000)setTimeout( () =&gt; { execCommand(commands[i]) } , 3000)

标签: javascript node.js arrays loops timeout


【解决方案1】:

我会让execCommand 返回一个承诺,这样你就知道什么时候完成了;您不能依赖超时(如果任务花费超过 3 秒怎么办?),并且由于大多数命令的完成速度会比这快很多,超时会不必要地拖延。

这里是 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...
}

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

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

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

【讨论】:

  • await execCommand(commands); 的意思是:await execCommand(command);
  • 它仍然没有等待上一个命令完成。在最后一个命令完成之前,这些命令似乎仍在运行?
  • @user3180997 - 是的,execCommand(commands) 本来是execCommand(command),对此感到抱歉。根据the documentationexec 在进程退出之前不会调用它的回调。因此,如果您认为承诺在此之前已经完成,它表明以下三件事之一:1. 您还没有完全正确地实现上述内容(道歉:-)),或 2. 命令 is 完整且您所看到的有不同的解释,或者...
  • ... 3. 您正在调用的进程只是 启动 另一件事,它不会阻止您正在创建的进程终止,并在背景。但是上面的execCommand肯定会等待它开始退出的进程,至少根据Node.js文档是这样的。
  • 不用担心,只是想仔细检查一下。好吧,当我将cd ../ 作为我的第一个命令并将pwd 作为我的第二个命令时,pwd 会打印出我当前所在的目录,而不是cd ../ 应该将我放入的目录。所以我认为要么没有运行cd ../,要么pwd先运行。
【解决方案2】:

目前,您不能保证在调用下一个命令时会完成上一个命令。您会在 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);
    }
})();

【讨论】:

  • 感谢您的回复。它仍然没有等待上一个命令完成。在最后一个命令完成之前,这些命令似乎仍在运行?
  • 呃,我不明白怎么回事,因为一切都返回一个 Promise 并且按顺序是awaited
  • 我知道,我已经一个接一个地添加了cd ../pwd,并且在cd发生之前pwd就打印出来了:/
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-28
  • 2017-10-22
  • 2014-12-08
  • 1970-01-01
  • 2022-07-19
  • 1970-01-01
相关资源
最近更新 更多