Revealing CoffeeScript’s Best Kept Secret
Iterables, Async Iterables, Generators and Async Generators
Call me a dinosaur, but I still consider many features of CoffeeScript superior to TypeScript, ES6 or any other language that compiles to JavaScript. There’s just one thing that started to annoy me. I couldn’t figure out how to deal with Iterables, and async generators. Somehow, Google didn’t help me in finding out how to do it, and the documentation on https://coffeescript.org/ is, well, “sparse”.
Iterators
Let’s start with Iterators. Iterators are easy. All you need is something with a next()
method that returns an object holding the value and an indication if we should proceed . In the example below, range
is a function accepting an upper and lower bound, producing an iterator. Neither JavaScript nor CoffeeScript have anything baked into the language for iterating over the elements, so you need something that looks a little clumsy to iterate through all elements.
Iterables
Iterables are where the fun starts. Iterables are simply objects with a method that produces an iterator. The name of that method is defined by the standard. Interestingly, it’s name is not a String
but a Symbol
instead. Which makes it challenging, if you missed out on a feature of the latest version of CoffeeScript that is mentioned only once, and briefly mentioned in the change log.
This would be the naive approach. In this particular case, I’m using CoffeeScript’s string interpolation solution for defining the key based on a constant defined somewhere else, in this case in Symbol.iterator
.
That doesn’t work, and if you realize that the key is expected to be a Symbol
, that makes perfect sense. Luckily, the latest versions of CoffeeScript actually allow you to use the same syntax for defining keys dynamically as you can find in ES6:
With that, we can now use the for-comprehension over iterables as a more convenient way to list all the values in the range.
Generators
This is the easy bit. CoffeeScript has support for writing generators, omitting some of the boilerplate JavaScript code:
Async Generators
Unfortunately, in many of the circumstances, your data will be the result of something getting retrieved asynchronously. Let’s change our range function to return Promises of values instead:
Note the await in for await i from range(0, 10)
.
Async Iterable
There may be cases in which you cannot use an async generator. In that case, it’s worthwhile knowing that you can also implement an async Iterable: