I have a function that looks like this:
function populateMap(directory: string, map, StringMap) {
fs.promises.readdir(directory).then(files: string[]) => {
files.forEach(file: string) => {
const fullPath = path.join(directory, file);
fs.stat(fullPath, (err: any, stats: any) => {
if (stats.isDirectory()) {
populateFileMap(fullPath, fileMap);
} else {
fileMap[file] = fullPath;
}
});
});
});
}
What I want to do is recursively walk through the parent directory and store a map of file names to their paths. I know this is working because if I put a console.log(fileMap) under fileMap[file] = fullPath, after the deepest file in the directory, the list is properly populated.
In the file that calls this function, I want to be able to have the full map as such
function populateMapWrapper(dir: string) {
const fileMap: StringMap = {};
populateMap(dir, fileMap);
//fileMap should be correctly populated here
}
I've tried making populateMap asynchronous, adding a .then() to where it's called in the wrapper function, but if I console.log(fileMap) in the then() function, the fileMap is empty.
I'm not sure if this is because of how javascript passes variables or if there's a gap in my understanding of promises, but I'm wondering if there's an alternative way to do this.
One problem is that fs.stat
doesn't return a promise. You need to also use fs.promises.stat
. Also, when working with promises be careful about using forEach
, because it doesn't await
for each of the forEach
callbacks. You could instead use map
with Promise.all()
One solution:
function populateMap(directory: string, map) {
return fs.promises.readdir(directory).then((files: string[]) => {
return Promise.all(
files.map((file: string) => {
const fullPath = path.join(directory, file);
return fs.promises.stat(fullPath).then(stats => {
if (stats.isDirectory()) {
return populateMap(fullPath, map);
} else {
map[file] = fullPath;
}
})
}))
})
}
Then you would have to use await
in the wrapper:
async function populateMapWrapper(dir: string) {
const fileMap: StringMap = {};
await populateMap(dir, fileMap);
//fileMap should be correctly populated here
}
However, a more readable solution would be to make use of await
whenever possible. Something like:
async function populateMap (directory: string, map) {
const files = await fs.promises.readdir(directory)
for (const file of files) {
const fullPath = path.join(directory, file)
const stats = await fs.promises.stat(fullPath)
if (stats.isDirectory()) {
await populateMap(fullPath, map)
} else {
map[file] = fullPath
}
}
}