Home > Software engineering >  Ensure that children functions have terminated
Ensure that children functions have terminated

Time:07-17

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);
}
  • Related