-
Notifications
You must be signed in to change notification settings - Fork 66
alternative iterators API #160
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Doesn't work yet, but future James will sort that out.
Works almost but is an absolute trainwreck. Will write it better now that i know the problem.
Of course, we need a queue! Now things work because we're trying to async iterate a synchronous task within an asynchronous task.
Sometimes it is possible we complete before the next iteration has run, so we leave a promise dangling or some such thing.
We currently exceed `maxFiles` and slice the result later. This doesn't work in an iterator since there is no end "result". This change instead makes the walker stop walking when `maxFiles` is met, so we already have the correctly sized result set.
* refactor: return an `AsyncGenerator` instead * chore: update test
Seems a waste jumping through hoops with sync-async when we can just inline cleaner logic.
state.visited.push(crawlPath); | ||
|
||
try { | ||
const entries = await promisify(fs.readdir)(crawlPath || ".", { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't this too expensive? it's promisifying the function each time it calls a directory
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there's a TODO somewhere saying this exact thing
i did already know this when writing it, but wasn't sure at the time what to do with it yet. since someone can change the fs
under the hood, we can't really hold a promisified reference around in case they change it. or we do, and just assume nobody will mutate things under us (my preferred solution)
3rd option is we change FSLike
to include promises
- so custom FS implementations must provide some async methods then
I was able to get a prototype working with full iterator support using |
this is a "full iterator", switching it to use do you mean you made a new branch unrelated to this and my other one? if that's the case, feedback would have been nice as these two branches took a lot of effort. and every comment that's been made was stuff i was already well aware of, but didn't want scope creep. i just needed to know we were open to larger change and would have done it i just wish you said sooner so i didn't spend so much time building this 😅 |
Not completely unrelated since the whole thing is still based on your PR (and idea). I was experimenting yesterday with your PR trying different approaches and came upon it. Still not sure how it performs or even if it is merge worthy yet.
I am sorry if the above comment came across as me undermining your work. My intention was to share a different approach to the same problem and see if we can reach a better solution overall. Especially since its a fundamentally different approach, I couldn't just leave a review. I hope that clarifies things. |
No worries at least this is still somewhat useful to help us figure out how we want to implement this To be honest, I think we need to rework the various push functions. Split them into test functions ("should we push this"), and push functions (actually push it). Then the iterator can share the same tests but do its own pushing (yielding). If we switch to opendir, we won't need any of the push functions but will need the test functions (to avoid duplicating conditional logic) |
@43081j that is certainly a design limitation right now. The main idea behind this approach was to reduce branching on the hot paths e.g. if you don't use a filter, the push function should be inlined by the engine. Is there a way to separate the "check" part and the "push" part without introducing branching at the callsite? |
Maybe we can have a "file filter" and a "directory filter" we build up at initialisation time, the same way we do with push functions now Then any time we want to push something, we call the appropriate filter and just directly push (or have a separate push function) So basically you would |
I see no way to |
yes we would need a condition either way, like this: if (this.filterFileFn(path)) {
yield path;
} where: filterFileFn = filterFile.build(options, state);
// and elsewhere
const allFilter = () => true;
function build(options, state) {
if (!options.filters) {
return allFilter;
}
// etc...
} is your concern that the non-iterator code can bypass this because it won't have a condition? it'll just always there's not really a clean way to achieve that when yielding i think. but a no-op function that is always true should be pretty fast |
an alternative to #154
draft until one day we can settle on a direction. probably end up with a 3rd branch once this one has been reviewed