2

Let me prefix the question with a note that I'm tagging the question with deno and javascript as I'm still getting my head around generator functions, iterators and some other newer async JS features, so I'm not exactly sure if this is more an issue with Deno API, or Javascript.

So, Deno.readDir() returns an AsyncIterable, and all the examples I've seen process the results with for await loop like so:

for await (const dirEntry of Deno.readDir("/")) {
  console.log(dirEntry.name);
}

For await loop is not good for my use case as I need to interleave traversing the directory with another async loop. I'd rather use something like let reader = Deno.readDir("/"); then inside my other asynchronous code, call something like let nextdir = await reader.next(); as needed.

I can't do that, as the result of Deno.readDir("/") does not implement next();

With pretty much trial and error, guesswork and trying everything under the sun I was able to make it work like this:

   let reader = Deno.readDir(source);
   let readeriter = reader[Symbol.asyncIterator](); // <-- the weird part

   // other code here between...

   let nextdir = await readeriter.next(); // works!
     

The questions is, the above code, specifically the part reader[Symbol.asyncIterator]() looks so ugly and hacky it can't be how I'm supposed to use that API, or can it? I'm struggling to understand what that line actually does. I get a feeling there must be cleaner way to consume the result of readDir (other than for...await).

I can't understand why readDir result does not directly implement next(), as the result, AsyncIterable is obviously something that is intended to be iterated over. Why is that?

This is not opinion, but an honest question because I'm a bit confused with all this generator-iterator business. Could Deno in theory implement next() right in the AsyncIterable so that one doesn't need to jump through hoops to get it?

yaku
  • 3,061
  • 2
  • 19
  • 38
  • 1
    "*AsyncIterable is obviously something that is intended to be iterated over*" - the difference from being an iterator (implementing `.next()` itself) is that it could be iterated multiple time, by calling `const readerIter2 = reader[Symbol.asyncIterator]();` again. See also [what is the difference between an iterable and iterator?](https://stackoverflow.com/q/59458257/1048572) and [Why do we need both iterable and iterator concepts?](https://stackoverflow.com/q/36543585/1048572) – Bergi Apr 11 '21 at 16:25
  • 1
    Also, no, [calling `[Symbol.asyncIterator]()` is not hacky](https://stackoverflow.com/q/56258471/1048572), it's how the protocol is supposed to work - and it's not ugly in my eyes either. If you want a nice syntax, use `for await … of` :-) – Bergi Apr 11 '21 at 16:40
  • 2
    not worth a full-fledged answer but you could inline it like `Deno.readDir("/foo")[Symbol.asyncIterator]();` to avoid the intermediary variable but unfortunately that's the canonical way to convert an Iterable into an Iterator. Since `Deno.readDir` is a stable api it's unlikely it'll change in the future -- you can try opening an issue, if you bring a good case it might get labelled for 2.0 but it's a long shot – William Perron Apr 11 '21 at 20:09
  • @Bergi ok, thank you for explanations and links, they are very helpful I think I'm starting to "get" it. Makes sense that one could need to go over an iterable multiple times, in which case need to create multiple iterators from the iterable. And good to know that's how it's supposed to be created, ugly or not :) – yaku Apr 11 '21 at 20:12
  • @WilliamPerron thank you. I don't think I'm qualified at this time to have on opinion how the API should be, I'll accept it is what it is :) Maybe the example code in Deno docs could be extended include a note about [Symbol.asyncIterator]() as it's such a core function and maybe not familiar to many of the folks coming from Node.js side. – yaku Apr 11 '21 at 20:20

0 Answers0