- Instead of modifying prototype, which would pollute the environment for any other modules to work with - use regular old functions
- Instead of spreading the iterable to an array and then calling `.forEach` on it, I would just iterate the source generator directly, it would look like:
for (const node of xpath(el, 'xpath')) {
node.textContent = node.textContent.replace("Hello", "Greetings")
}
I honestly don't see much the value of generators here. we can simply return all the results directly from the xpath function that you created. Are there any use case where you would want to stream the elements one by one ? you have got all the results in memory anyways inside the xpath function
> you have got all the results in memory anyways inside the xpath function
Not necessarily, that's an implementation detail. The result of an XPath query is not an array, it's some object that accepts integer indices for lookup.
If you were to return an array, there would be no way to break out of iteration early without paying for every lookup. For something like XPath, breaking out early is a likely scenario.
To me, it seems to support lazy evaluation much better with a generator.
Additionally composability seems improved, due to all functions being able to be written in a way where they themselves become generators, so you apply all steps only to each result you retrieve. Potentially saving quite a bit of execution time, if you don't need the full collection.
That's not entirely true if you need to support IE, in which case Babel (or whatever compiler you're using) will generate extra code for the `for...of` loop. But even then, I would still recommend using it for the reasons you mentioned.
Generators probably only add overhead here since there is nothing asynchronous happening. You can just as well wrap the first version in a function and return an array:
Generators/Iterators are not just for asynchronous code. Generators may be a bit slower than iterating over an array, but arrays use more memory and put more pressure on the garbage collector.
True, though I suspect the impact in the case of a node list is negligible; in quick tests I couldn't detect any meaningful difference even while looping through 10k DOM elements.
I'd like to take this opportunity to plug my npm package `fromable`(https://github.com/baron816/fromable) which makes it easy an efficient to transform iterables and output the result to any collection. It's kind of a `Array.from` meets transducers.
Pretty sure it will not. Nothing is lazy in JS by default:
> One caveat of spreading iterables: JavaScript creates an array out of the elements of the iterable. That might be very wasteful for extremely large collections. For example, if we spread a large collection just to find an element in the collection, it might have been wiser to iterate over the element using its iterator directly.
> And if we have an infinite collection, spreading is going to fail outright.
> Pretty sure it will not. Nothing is lazy in JS by default:
It really depends on the logic written. You can solve the same thing in many different ways, the way chosen by the author doesn't seem to be a lazy one.
Not the language's fault. It's simply doing what is asked of it.
I don't think any language has a lazy spread operator. Haskell is the only language I know off lazy enough to conceivably have one, but it doesn't accept arbitrary lists of arguments (unless you count having an actual list as an argument, which is the better option if you want laziness really).
- Instead of modifying prototype, which would pollute the environment for any other modules to work with - use regular old functions
- Instead of spreading the iterable to an array and then calling `.forEach` on it, I would just iterate the source generator directly, it would look like: