我正在尝试使用Node从多个服务器检索SFTP列表。我正在使用ssh2-sftp客户端库,并尝试使用futted 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列表获取的异步性质,是否有更干净的方法?
您需要实际返回函数中的promise-getDirectoryListing()
没有返回任何内容。因此,您正在将一个充满undefined
的数组传递给Promise.allSettled()
试试这个:
function getDirectoryListing(config) {
let sftp = new Client();
return sftp.connect(config)
.then(() => {
// ...stuff
}
您的getDirectoryListing实际上并没有返回promise。像这样的东西应该对你有用:
#!/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)));
要使代码正常工作,只需要从函数getDirectoryListing()
返回promise,以确保语句执行的正确顺序。
您的解决方案:
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)));
一旦事件循环完成了promise,它就会将它们添加到回调队列中,然后对它们进行处理。因此,行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 [];
});
如果你想了解更多关于事件循环的信息,你可以观看这段精彩的视频。