0

我正在尝试使用 Node 从多个服务器检索 SFTP 列表。我正在使用 ssh2-sftp-client 库,并且正在尝试使用 futzed Promise.all() 来处理异步连接。

SFTP 服务器存储在配置文件 ( servers.config) 中,如下所示:

{
  "myhost1": {
    "host": "sftp.myhost1.com",
    "port": "22 ",
    "username":  "userid1",
    "password": "password1"
  },
  "myhost2": {
    "host": "sftp.myhost2.com",
    "port": "22 ",
    "username":  "userid2",
    "password": "password2"
  },
  ...
}

我的代码看起来像这样......

#!/node
let fs = require('fs');
let Client = require('ssh2-sftp-client');

// which servers should we process?
const serverList = fs.readFileSync('./servers.config', {encoding:'utf8', flag:'r'});
const servers = JSON.parse(serverList);

const servers_to_process = Object.keys(servers);

function getDirectoryListing(config) {
  let sftp = new Client();
  sftp.connect(config)
    .then(() => {
      return sftp.list('/');
    })
    .then(data => {
      console.log('Data retrieved for: ',config.host);
      //console.log(data);  // Line B
      sftp.end();
      return data;
    })
    .catch(err => {
      console.log('Error for: ',config.host);
      return [];
    });
}


const processes_to_run = [];

// generate array of promises to run
servers_to_process.forEach( key => {
    log('==========================');
    log("Provider: "+key+"; "+timestamp);
    processes_to_run.push(getDirectoryListing(servers[key]));
  });


// wait for all the promises to resolve...
Promise.allSettled(processes_to_run).
  then((results) => results.forEach((result) => console.log(result)));

我没有得到的是来自 A 行的任何控制台记录的数据......但是如果我取消注释 B 行,我会异步获取每个列表。

输出看起来像这样:

JSON file read correctly
==========================
Provider: myhost1; 01/06/2021, 14:57:25
==========================
Provider: myhost2; 01/06/2021, 14:57:25
{ status: 'fulfilled', value: undefined }
{ status: 'fulfilled', value: undefined }
Data retrieved for:  sftp.myhost1.com
Data retrieved for:  sftp.myhost2.com

所以,很明显我正在放弃从承诺中返回数据......

这是在处理之前将所有列表放入数组的正确方法吗?鉴于 SFTP 列表获取的异步性质,是否有更清洁的方法?

4

3 回答 3

2

您需要从您的函数中实际返回承诺 -getDirectoryListing()没有返回任何内容。因此,您正在传递一个充满undefinedto的数组Promise.allSettled()

尝试这个:

function getDirectoryListing(config) {
  let sftp = new Client();
  return sftp.connect(config)
    .then(() => {
    // ...stuff
}
于 2021-06-01T15:15:26.183 回答
1

您的 getDirectoryListing 实际上并没有返回承诺。像这样的东西应该适合你:

#!/node
let fs = require('fs');
let Client = require('ssh2-sftp-client');

// which servers should we process?
const serverList = fs.readFileSync('./servers.config', {encoding:'utf8', flag:'r'});
const servers = JSON.parse(serverList);

const servers_to_process = Object.keys(servers);

//Ensure this is returning a promise by making it async
//and controlling the flow with await rather than callbacks
async function getDirectoryListing(config) {
    let sftp = new Client();
    await sftp.connect(config)
    let list = await sftp.list('/');
    console.log('Data retrieved for: ',config.host);
    console.log(list);  // Line B
    sftp.end();
    return list;
}


const processes_to_run = [];

// generate array of promises to run
servers_to_process.forEach( key => {
    console.log('==========================');
    console.log("Provider: "+key+"; "+Date.now());
    processes_to_run.push(getDirectoryListing(servers[key]));
  });


// wait for all the promises to resolve...
Promise.allSettled(processes_to_run).
  then((results) => results.forEach((result) => console.log(result)));
于 2021-06-01T18:26:24.897 回答
0

要使您的代码正常工作,您只需要从函数返回承诺,getDirectoryListing()以确保语句执行的正确顺序。

你的修复:

function getDirectoryListing(config) {
  let sftp = new Client();
  return sftp.connect(config) // just add a return here
    // ...rest code will be same
}

但你也必须明白为什么会出现这种意想不到的结果。(如果您想了解幕后发生的事情,请阅读本节)

当您调用该方法getDirectoryListing()时,您将 Promise 添加到事件循环并返回undefined。由于processes_to_run数组已满,undefined因此无法在processes_to_run数组中执行。这就是为什么执行首先转到console.log(result).

// wait for all the promises to resolve...
Promise.allSettled(processes_to_run).
  then((results) => results.forEach((result) => console.log(result)));

一旦事件循环完成承诺,它会将它们添加到回调队列中,然后处理它们。因此,A 行随后被打印。

  sftp.connect(config)
    .then(() => {
      return sftp.list('/');
    })
    .then(data => {
      console.log('Data retrieved for: ',config.host); // Line A
      //console.log(data);  // Line B
      sftp.end();
      return data;
    })
    .catch(err => {
      console.log('Error for: ',config.host);
      return [];
    });

如果你想了解更多关于事件循环的信息,你可以观看这个精彩的视频

于 2021-06-04T19:40:06.183 回答