I have a function that walks recursively over a folder, performing an expensive operation on each file. So far, the function was simple and pretty:
import fs from 'fs';
async function openPath(path) {
// Path is a directory
if (fs.statSync(path).isDirectory()) {
let subPaths = fs.readdirSync(path);
for (const file of subPaths) await openPath(path + '/' + file);
// Path is a file
} else await expensiveOperationWithDB(path);
}
Realizing that I only needed file processing to be synchronized when the files were on the same directory, I came up with this ugly function:
async openPath(path, isFile) {
if (isFile) await expensiveOperationWithDB(path);
else {
const subPaths = fs.readdirSync(path);
for (let newPath of subPaths) {
newPath = path + '/' + newPath;
if (fs.statSync(path).isFile()) {
await this.openPath(newPath, true);
} else {
this.openPath(newPath, false);
}
}
}
}
However, the function should return only when its children functions have terminated. How can this be implemented?
You'll want to collect all the promises from any called functions into an array and use Promise.all on them.
import fs from 'fs/promises';
async function openPath(path) {
const stat = await fs.stat(path);
if (stat.isDirectory()) {
return openDirectory(path);
} else if (stat.isFile()) {
return expensiveOperationWithDB(path);
}
}
async function processDirectoryFiles(filePaths) {
for (const path of filePaths) {
await expensiveOperationWithDB(path);
}
}
async function openDirectory(path) {
const names = await fs.readdir(path);
const pathStats = await Promise.all(names.map(name => {
const newPath = path + '/' + file;
const stat = await fs.stat(newPath);
return {
path: newPath,
isFile: stat.isFile(),
isDirectory: stat.isDirectory();
};
}
const promises = [];
const filePaths = [];
for (const {path, isDirectory, isFile} of pathStats) {
if (isDirectory) {
promises.push(openDirectory(path));
} else if (isFile) {
filePaths.push(path);
}
}
promises.push(processDirectoryFiles(filePaths));
return Promise.all(promises);
}