-
-
Save qwtel/fd82ab097cbe1db50ded9505f183ccb8 to your computer and use it in GitHub Desktop.
// Node 8+ | |
// -------------------------------------------------------------- | |
// No external dependencies | |
const { promisify } = require('util'); | |
const { resolve } = require('path'); | |
const fs = require('fs'); | |
const readdir = promisify(fs.readdir); | |
const stat = promisify(fs.stat); | |
async function getFiles(dir) { | |
const subdirs = await readdir(dir); | |
const files = await Promise.all(subdirs.map(async (subdir) => { | |
const res = resolve(dir, subdir); | |
return (await stat(res)).isDirectory() ? getFiles(res) : res; | |
})); | |
return files.reduce((a, f) => a.concat(f), []); | |
} | |
// Usage | |
getFiles(__dirname) | |
.then(files => console.log(files)) | |
.catch(e => console.error(e)); | |
// Node 10.10+ | |
// -------------------------------------------------------------- | |
// Updated for node 10+ with even more whizbang: | |
const { resolve } = require('path'); | |
const { readdir } = require('fs').promises; | |
async function getFiles(dir) { | |
const dirents = await readdir(dir, { withFileTypes: true }); | |
const files = await Promise.all(dirents.map((dirent) => { | |
const res = resolve(dir, dirent.name); | |
return dirent.isDirectory() ? getFiles(res) : res; | |
})); | |
return Array.prototype.concat(...files); | |
} | |
// Note that starting with node 11.15.0 you can use | |
// `files.flat()` instead of `Array.prototype.concat(...files)` | |
// to flatten the array. | |
// Node 11+ | |
// -------------------------------------------------------------- | |
// The following version uses async iterators. | |
const { resolve } = require('path'); | |
const { readdir } = require('fs').promises; | |
async function* getFiles(dir) { | |
const dirents = await readdir(dir, { withFileTypes: true }); | |
for (const dirent of dirents) { | |
const res = resolve(dir, dirent.name); | |
if (dirent.isDirectory()) { | |
yield* getFiles(res); | |
} else { | |
yield res; | |
} | |
} | |
} | |
// Usage has changed because the return type is now an | |
// async iterator instead of a promise: | |
(async () => { | |
for await (const f of getFiles('.')) { | |
console.log(f); | |
} | |
})() | |
// I've written more about async iterators here: | |
// https://qwtel.com/posts/software/async-generators-in-the-wild/ |
That doesn't work properly because it returns unresolved promises. This should work:
What an oversight. I've fixed it in the gist. Thanks for posting a correction.
@qwtel
Great article/examples. Despite having years of embedded multitasking OS development background, Promises just have so many gotchas and ambiguities from my perspective. Still anyway question:
I'm thoroughly confused about the following statements:
const res = resolve(dir, subdir)
Everything I have read is very clear that Promise.resolve() can only return one value. Using any object/array can obviously get around that.
What paradigm allows the above resolve with two parameters? More importantly resolve is undefined (executing under Thunderbird)
with a directory iterator so I had to do some adaptation, but I don't think the resolve question changes.
Thanks
@cleidigh
@cleidigh resolve
here is actually path.resolve
which takes multiple paths and returns a single value.
Note the const { resolve } = require('path');
in the snippet.
@mrchief
Thanks! I got completely hung up on the resolve and missed the reference to the path module.
I'm trying to adapt this using a Mozilla Thunderbird DirectoryInterator as opposed to readDir.
I have been trying every possible combination of promise handling , but I am obviously flummoxed.
Removing the unnecessary path combination in my case still fails.
I'm going to keep experimenting but I may post my best if I can't get it. This approach is certainly what I need to use just with some tweaks that Confound me.
For example "Node 10.10+":
That doesn't work properly because it returns unresolved promises. This should work: