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?
CodePudding user response:
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);
}