♪ (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
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
the most recent one you've iterated to,
and the
you're done with the most recent value,
so forget it and load the next one into
Iterable
It just means a class that can give you
an Iterator of a particular type.
In this case, since
it needs to have an iterator property
that returns an
One of the cool things about Iterables
is that you can stick them in a
If I have one of these
I can use
one after the other.
Dart will automatically call
so I can just write a simple loop.
Some of the handy methods you're used to with Streams,
like
can also be used with Iterators.
They just return a new Iterator,
which you can also loop over using
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
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
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
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
or
you can't be bothered with an actual loop.
One last thing before we move on to async generators.
The
If I had chosen to make
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
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
to yield a whole Iterable, one value at a time--
no loop required.
All right, so that's
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
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
Notice that the function is calling
to wait on the Future from
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
And just like any Stream, I can use
listen
in order to take advantage of those values.
Also, if I wanted to rewrite my
I can still use
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
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) ♪