♪ (music) ♪ Hey, everybody, and welcome to the final episode
in our Flutter in Focus series on asynchronous coding in Dart. In this episode, we're
going to cover generators, which are functions that can produce not one, but multiple values. They can do that either
synchronously or asynchronously. You may remember this chart
from one of the earlier videos. A synchronous generator will
have a return type of Iterable, while an asynchronous one
has a return type of Stream. As you'll see in a
second, the code for both looks mostly the same. The big difference is that
synchronous generators are expected to produce values on demand, right away, so they can't wait on Futures or Streams. Async generators, on the other hand, are allowed to take their sweet,
sweet time producing values, so they can use the <i>await</i> keyword. One other thing to note before going on, is that while generators
are really handy when you need them, you're probably not going
to need them very often. In the past year of coding with Flutter, I've used an async generator just once. That said, when you do see
that right spot and think, "This is it, this is the place
where a generator can save me a bunch of boilerplate, and I know how to do it
because I am a Dart rockstar"-- it's very satisfying. So, let's start with
synchronous generators, but first, a quick review
of Iterables and Iterators. An Iterator is a thing
that lets you iterate, one at a time,
over a series of values. It's a very simple interface. There's a <i>current</i> property,
which returns the current value, the most recent one you've iterated to, and the <i>moveNext</i> method,
which tells the Iterator, you're done with the most recent value, so forget it
and load the next one into <i>current.</i> <i>Iterable</i> is also a simple interface. It just means a class
that can give you an Iterator of a particular type. In this case, since <i>MyStrings</i>
extends <i>Iterable String,</i> it needs to have an iterator property that returns an <i>Iterator String.</i> One of the cool
things about Iterables is that you can stick
them in a <i>for/in</i> loop. If I have one of these<i>
MyStrings</i> objects, for instance, I can use <i>for/in</i> to loop
over each of its values, one after the other. Dart will automatically call<i>
moveNext</i> and <i>current</i> for me, so I can just write a simple loop. Some of the handy methods
you're used to with Streams, like <i>where</i> and <i>map,</i> can also be used with Iterators. They just return a new Iterator, which you can also
loop over using <i>for/in.</i> So, how do you create a generator
that returns one of these Iterables? Well, first, declare a function, and make sure it has
a return type of Iterable. Here I'll define
one that's going to return a range of numbers from start to finish. Next, use the sync*
keyword to mark the function as a synchronous generator. This is just a way to tell
Dart that the function is going to produce multiple values on demand. Once that's done,
I just need to use the <i>yield</i> keyword to yield-- each of the values in order. Yield is kind of like return,
but it doesn't end the function. Instead, it provides a single value and waits for the caller
to request the next one. Then, it picks up executing again. In this case, doing
another round of the loop. When it hits <i>yield,</i> it returns
that value and waits again. Interestingly, this function doesn't
really begin executing at all, until someone starts iterating
over the Iterable that it returns. It's providing values
synchronously, on demand, so the first time someone tries
to get one of those values, that's when it kicks on
and runs until it hits <i>yield.</i> And since that return
value is an Iterable, you can do all the usual stuff,
like looping over the values. This code will print out the numbers
from 1 to 10, for example. You can also use
the normal methods Iterable has, like <i>where,</i> if you decide
you only want the even numbers, or <i>forEach,</i> if you decide you can't be bothered with an actual loop. One last thing before we move on
to async generators. The <i>yield</i> keyword has a variant
for yielding other Iterables. If I had chosen to make <i>getRange</i>
recursive, for example-- and I'll give you a second
to read the code, because that's a big change-- I have to loop over the Iterable
returned by the inner call to <i>getRange</i> just so I can
yield values one at a time. In the first call,
that loop executes nine times. In the next one, eight. Then seven, six. And you can see how my simple function
just became quadratic. Fortunately, there's <i>yield </i>*,
which you can use to yield a whole Iterable,
one value at a time-- no loop required. All right, so that's <i>yield, yield </i>*<i>,</i>
and synchronous generators. Next up are async generators,
which work almost the same, only they return Streams
and they can yield values when they decide they're ready. Let's say, hypothetically,
we lived in a world where all math had to be done on a server, and so we had a function like
this that would make a network call to double a number for us. Horrifying as that may be, if you saw the other episodes
in the series, you know how to call that method once, and use <i>then,</i> to take action
when the Future completes. But what if I want to get
a bunch of those values, one after another, in a Stream? That's where I can use
an async generator function. First, I declare a function
that returns a Stream of the right type. Then, I add the async* keyword to mark the function
as an async generator. Then, I use <i>yield</i> to yield
the values as I receive them. Notice that the function is calling <i>await</i> to wait on the Future from <i>fetchDouble.</i> If this were a synchronous generator,
that wouldn't compile. Now I've got a method
that produces the doubles of a range of numbers, one at a time, waiting on <i>fetchDouble</i> for each one. And just like any Stream,
I can use <i>where, map, forEach,</i> <i>listen</i>-- whatever I want-- in order to take
advantage of those values. Also, if I wanted to rewrite
my <i>fetchDoubles</i> function to be recursive, I can still use <i>yield </i>* with a Stream to keep my code tight and efficient. All right. That's all we have for this video, and all we have
for this Flutter in Focus series. Hopefully, it's demystified
some of the core concepts of async coding for you, because once you get used
to handling Futures and Streams, designing our apps to react
to asynchronous data, you will wonder
how you ever got by without it. In the meantime,
head to <i>dart.dev</i> and <i>flutter.dev</i> for more info on Dart and Flutter, and we'll see you next time. ♪ (music) ♪ Hey, if you enjoyed that video,
try these others or subscribe to the
Flutter channel. It's Google's new
portable UI tool kit. There's a button
around here somewhere. ♪ (music) ♪
this was the best and most practical series imo from the flutter channel