I'm using node-ftp to download some files from an FTP server; due to nodeback difficulty and nested calls I'm promisified some of the calls, hoping that this will help get me out of pyramid code hell, but I think I'm still mis-applying something.
Before I get to my code pasted below, conceptually the pseudo-code of what I'm trying to do is:
foreach directory in a list:
list files in directory
filter to only files of interest
download those files, save them to /tmp/whatever.
The objective is to end with a list of files downloaded and local paths.
I have code that works, but it's god-awful ugly and is not living up to the "promise of promises". How can I re-arrange this to be more like the pseudo code above, and aggregate the results? Part of the trouble is that the "list files in directory" is a promise, and each download returns a promise of a data stream. So unless I do this smartly, it's a 3-level nested array of promises that all have to be resolved, with the very bottom level promise results the intended return value.
//Main handler: when the connection is ready, start doing stuff.
c.on('ready', function() {
config.clientCheckPaths
.map(function (lookInPath) {
console.log('Examining ' + lookInPath);
// Go get directory contents.
listAsync(lookInPath)
.then(function (files) {
console.log('Files: ' + files.map(function (i) { return i.name; }));
var wantedFiles = files.filter(function (item) {
return config.isFileWanted(item.name);
});
// Filter it down to only the files we want.
var arrayOfDownloadPromises = wantedFiles.map(function (x) {
// For each file, go download it.
return new Promise(function (resolve, reject) {
var fullPath = lookInPath + '/' + x.name;
console.log("Wanted file: " + lookInPath + '/' + x.name + ", " + Object.keys(x));
getAsync(fullPath)
.then(function (stream) {
// With the stream, name it something reasonable on the disk.
var localFileName = '/tmp/' + fullPath.replace(/[^A-Za-z0-9\.]/g, '_');
console.log("Writing stream to " + localFileName);
stream.once('close', function() {;});
stream.pipe(fs.createWriteStream(localFileName));
resolve(localFileName);
}, function(err) {
var msg = 'Getting file ' + fullPath + ' failed with ' + err;
console.error(msg);
reject(msg);
})
});
});
// We then need to re-join all of the download promises and proceed
// when they're all finished.
return Q.all(arrayOfDownloadPromises);
}, function(err) {
console.error('Listing directory ' + lookInPath + ' failed with ' + err);
});
})
.map(function (promise) {
console.log('Final: ' + promise);
});
});
This code performs the right actions now, but doesn't aggregate the results correctly, and is ugly. How to refactor this when I have multi-level nested promises?
function(lookInPath)
- line 3 \$\endgroup\$ – Jaromanda X Dec 10 '15 at 2:44Promise
constructor antipattern! \$\endgroup\$ – Bergi Dec 10 '15 at 4:06