Ienumerable and yield return in C#... and in Javascript!? Iterators and generators
IEnumerable/IEnumerator - the iterator design pattern implemented in the C# language
C# started up with the IEnumerable interface, which exposed one method called GetEnumerator. This method would return a specialized object (implementing IEnumerator) that could give you from a collection of items the current item and could also advance. Unlike Java on which C# is originally based, this interface was inherited by even the basest of collection objects, like arrays. This allowed a special construct in the language: foreach. One uses it very simply:
for (var item in ienumerable) // do something. No need to know how or from where the item is exactly retrieved from the collection, just get the first, then the next and so on until there are no more items.
In .NET 2.0 generics came, along their own interfaces like IEnumerable<T>, holding items of a specific type, but the logic is the same. It also introduced another language element called yield. One didn't need to write an IEnumerator implementation anymore, they could just define a method that returned an IEnumerable and inside "yield return" values. Something like this:
public class Program
{
public static void Main(string[] args)
{
var enumerator = Fibonacci().GetEnumerator();
for (var i = 0; enumerator.MoveNext() && i < 10; i++)
{
var v = enumerator.Current;
Console.WriteLine(v);
}
Console.ReadKey();
}
public static IEnumerable<int> Fibonacci()
{
var i1 = 0;
var i2 = 1;
while (true)
{
yield return i2;
i2 += i1;
i1 = i2 - i1;
}
}
}
This looks a bit weird. A method is running a while(true) loop with no breaks. Shouldn't it block the execution of the program? No, because of the yield construct. While the Fibonacci series is infinite, we would only get the first 10 values. You can also see how the enumerator works, when used explicitly.
Iterators and generators in Javascript ES6
EcmaScript version 6 (or ES6 or ES2015) introduced the same concepts in Javascript. An iterator is just an object that has a next() method, returning an object containing the value and done properties. If done is true, value is disregarded and the iteration stops, if not, value holds the current value. An iterable object will have a method that returns an iterator, the method's name being Symbol.iterator. The for...of construct of the language iterates the iterable. String, Array, TypedArray, Map and Set are all built-in iterables, because the prototype objects of them all have a Symbol.iterator method. Example:
var iterable=[1,2,3,4,5];
for (v of iterable) {
console.log(v);
}
But what about generating values? Well, let's do it using the knowledge we already have:
var iterator={
i1:0,
i2:1,
next:function() {
var result = { value: this.i2 }
this.i2+=this.i1;
this.i1=this.i2-this.i1;
return result;
}
};
var iterable = {};
iterable[Symbol.iterator]=() => iterator;
var iterator=iterable[Symbol.iterator]();
for (var i=0; i<10; i++) {
var v=iterator.next();
console.log(v.value);
}
As you can see, it is the equivalent of the Fibonacci code written in C#, but look at how unwieldy it is. Enter generators, a feature that allows, just like in C#, to define functions that generate values and the iterable associated with them:
function* Fibonacci() {
var i1=0;
var i2=1;
while(true) {
yield i2;
i2+=i1;
i1=i2-i1;
}
}
var iterable=Fibonacci();
var iterator=iterable[Symbol.iterator]();
for (var i=0; i<10; i++) {
var v=iterator.next();
console.log(v.value);
}
No, that's not a C pointer, thank The Architect, it's the way Javascript ES6 defines generators. Same code, much clearer, very similar to the C# version.
Uses
OK, so these are great for mathematics enthusiasts, but what are we, regular dudes, going to do with iterators and generators? I mean, for better or for worse we already have for and .forEach in Javascript, what do we need for..of for? (pardon the pun) And what do generators help with?
Well, in truth, one could get away simply without for..of. The only object where .forEach works differently is the Map object, where it returns only the values, as different from for..of which returns arrays of [key,value]. However, considering generators are new, I would expect using for..of with them to be more clear in code and more inline with what foreach does in C#.
Generators have the ability to easily define series that may be infinite or of items which are expensive resources to get. Imagine a download of a large file where each chunk is a generated item. An interesting use scenario is when the .next function is used with parameters. This is Javascript, so an iterator having a .next method only means it has to be named like that. You can pass an arbitrary number of parameters. So here it is, a generator that not only dumbly spews out values, but also takes inputs in order to do so.
In order to thoroughly explore the value of iterators and generators I will use my extensive knowledge in googling the Internet and present you with this very nice article: The Hidden Power of ES6 Generators: Observable Async Flow Control which touches on many other concepts like async/await (oh, yeah, that should be another interesting C# steal), observables and others that are beyond the scope of this article.
I hope you liked this short intro into these interesting new features in ES6.
Comments
I just learned that you can pass a parameter value to the Generator.prototype.next() call. This value then gets returned by the yield statement into the flow of the generator. I wonder how we could build a c# equivalent? See https://stackoverflow.com/questions/37354461/how-does-generator-next-processes-its-parameter
Carl