soldair / node-walkdir

Walk a directory tree emitting events based on the contents. API compatable with node-findit. Walk a tree of any depth. Fast! Handles permission errors. Stoppable. windows support. Pull requests are awesome. watchers are appreciated.
MIT License
130 stars 22 forks source link

"end" event triggers before "file" event ? #36

Closed GopherJ closed 5 years ago

GopherJ commented 5 years ago
const walk = require('walkdir');
const fs = require('fs');

/**
 *
 * @param dir
 * @param {function} file
 * @param {function} end
 */
module.exports = (dir, file, end) => {
    const emitter = walk(dir);

    emitter.on('file', (path, stat) => {
        fs.readFile(path, 'utf8', (err, data) => {
            if (err) file(err, path, stat, null);
            else file(null, path, stat, data);
        });
    });

    emitter.on('end', end);
};
const filewalker = require('./filewalker')

filewalker('./', () => {
    console.log('file')
}, () => {
    console.log('end')
});

here is the output:

file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
end
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file
file

you can see that the end triggers before some "file" event

soldair commented 5 years ago

thanks for the bug repro. we should be able to fix this. if you'ed like to let me know =)

soldair commented 5 years ago

oh i see. there is a little bug in your code here.

you are calling fs.ReadFile in the on('path') handler. walkdir has already finished before your readFile has finished reading the data so you get file callbacks fired from inside the callback of readFile later than the end of walkdir.

you have to track how may readFiles you're running and if walkdir has ended and there are no more files to read trigger end on your file reader.

const walk = require('walkdir');
const fs = require('fs');

/**
 *
 * @param dir
 * @param {function} file
 * @param {function} end
 */
module.exports = (dir, file, end) => {
    const emitter = walk(dir);
   const readingFiles = 0
    const ended = 0
    emitter.on('file', (path, stat) => {
        readingFiles++;
        fs.readFile(path, 'utf8', (err, data) => {
            readingFiles--
            if (err) file(err, path, stat, null);
            else file(null, path, stat, data);
            if(!readingFiles && ended) end()
        });
    });

    emitter.on('end', ()=>{
        ended = true;
        if(!readingFiles) end()
    });
};

should fix your bug

a-barbieri commented 4 years ago

Hi @soldair. This thread is pretty old. Is there a more concise way to obtain the same output now?

soldair commented 4 years ago

let result = await walk.async('../',{return_object:true}) is the best. If you don't do any async work in the path handler you never get an end before you're done.

Remember this example mashes together 2 steps. Collecting paths, and processing them. this library doesn't have anything to do with how you process paths really. It only knows if you've emitted them all.

it would be possible to change a number of things such that if you return a promise from the eventHandler it could wait for you but i don't know that it should be done before a larger refactor to support/use fs.DirEnt

a-barbieri commented 4 years ago

Thanks @soldair.

Remember this example mashes together 2 steps. Collecting paths, and processing them. this library doesn't have anything to do with how you process paths really. It only knows if you've emitted them all.

That clarifies everything.

it would be possible to change a number of things such that if you return a promise from the eventHandler it could wait for you but i don't know that it should be done before a larger refactor to support/use fs.DirEnt

Maybe that's not priority one. It's also good to keep a KISS approach ;-)