【问题标题】:unable to exit my process after completion. NodeJS完成后无法退出我的进程。节点JS
【发布时间】:2026-01-12 18:55:01
【问题描述】:

我正在开发一个 Node JS 程序以连接到远程 SFTP 服务器以复制日志文件。我正在使用这个 NPM 包。 ssh2-sftp-client

const main = () => {
  const servers = ['server list'];
  servers.forEach((s) => {
    const sftp = new Client();
    // Connect to the server
    sftp.connect({
      host: s,
      port: '22',
      username: '',
      password: '',
    }).then(() => logger.log(`Connected to ${s}`))
      // get list of directories
      .then(() => sftp.list(rootLogPath))
      .then((dirs) => {
        dirs.forEach((d) => {
          const target = createTargetDirectory(d.name);
          // list all files in the directory
          sftp.list(`${rootLogPath}/${d.name}`)
            .then((files) => {
              // filter only today's files
              const todayFiles = files.filter((f) => f.type === '-' && moment().format('MMDDYYYY') === moment(f.modifyTime).format('MMDDYYYY'));
              // copy today's files into target
              todayFiles.forEach((f) => {
                sftp.get(`${rootLogPath}/${d.name}/${f.name}`, `${target}/${f.name}`)
                  .then(() => logger.log(`Copied ${f.name} from ${d.name} located on ${s}`));
              });
            });
        });
        return sftp.end();
      })
      .catch(() => logger.log(`Connection to ${s} failed`));
  });
};

main();

代码按预期工作,但问题是我无法终止会话。该程序只是在完成复制文件操作后等待。 sftp.end() 调用在复制完成之前过早关闭连接。如果我删除该程序,则在复制完成后等待。

我不确定在哪里写sftp.end() 行来终止程序。

编辑 1:使用 Promise.all 更新代码并立即使用 async/await。

将我的代码分成几部分,现在使用 async/await 以获得更好的可读性。程序现在结束正常。不再等待或挂起,但问题是文件没有被复制。我看到一条控制台消息“从源目录复制文件”。

const copyTodayFiles = async (src) => {
  try {
    let fileList = await sftp.list(`${rootLogPath}/${src}`);
    fileList = fileList.filter(
      (f) => f.type === '-' && moment().format('MMDDYYYY') === moment(f.modifyTime).format('MMDDYYYY'),
    );
    const target = createTargetDirectory(src);

    if (target) {
      fileList.forEach(async (f) => {
        try {
          await sftp.get(`${rootLogPath}/${src}/${f.name}`, `${target}/${f.name}`);
          logger.log(`Copied ${f.name}`);
        } catch (error) {
          logger.log(`Failed to copy ${f.name}`);
        }
      });
      console.log(`Copied files from ${src}`);
    }
  } catch (error) {
    logger.log(`Failed to read files from ${src}`);
    logger.log(error);
  }
};

const workOn = async (server) => {
  sftp = new Client();

  const config = {
    host: server,
    port: '22',
    username: '',
    password: '',
  };

  try {
    await sftp.connect(config);
    logger.log(`Connection to ${server} is successful.`);
    const logDir = await sftp.list(rootLogPath);
    Promise.all(logDir.map((d) => copyTodayFiles(d.name))).then(() => sftp.end());
    // logDir.forEach(async (d) => copyTodayFiles(d.name));
  } catch (error) {
    logger.log(`Connection to ${server} failed.`);
    logger.log(error);
  }
};

const main = () => {
  const servers = ['server list'];
  Promise.all(servers.map((s) => workOn(s)));
};

main();

【问题讨论】:

  • 您正在使用forEach 循环,它同步执行并且不保存函数返回值,并在forEach 返回时立即调用sftp.end()。在继续之前查找有关在异步代码中使用forEach 的问题。 :-)
  • 谢谢。会调查的

标签: javascript node.js sftp


【解决方案1】:

简而言之,当你有一个“链”承诺 (a.then(() => b.then(() =>...) 并且你想在它们的末尾做一些事情时,你只需将它添加到最后一个 .then 回调中。

但是,如果您要做出 多个 承诺,则需要等到所有承诺都完成,您可以使用 Promise.all (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) 来完成:

Promise.all([promise1, promise2]).then(...

为此,您需要将所有 forEach 调用转换为 map 调用,这将返回它们创建的承诺。一旦你这样做了,map 操作将返回一个包含这些承诺的数组,你可以将其与Promise.all 一起使用。

【讨论】:

  • 现在将我的代码拆分为函数并使用 async/await。请参阅编辑 1
  • 不幸的是,这仍然行不通。如果您想以这种方式做事,则需要切换到语言级别 for(let i = 0; i < fileList.length; i++) { 样式。你必须记住,即使你在一个函数上使用async,真正的意思是“这个函数返回一个承诺”。 .forEach 不知道如何处理 Promise,因此您要么不使用它,要么使用 map 并将返回的 Promise 数组传递给 Promise.all
  • 这帮助我解决了我的问题。谢谢!