Generator Functions - Flutter in Focus

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

this was the best and most practical series imo from the flutter channel

👍︎︎ 2 👤︎︎ u/dan-danny-daniel 📅︎︎ Jul 20 2019 🗫︎ replies
Captions
♪ (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) ♪
Info
Channel: Flutter
Views: 104,850
Rating: undefined out of 5
Keywords: Async generator, sync generator, await, asynchrony, coding in dart, dart, flutter, generator functions, Iterator refresh, how to use generator functions, Flutter in focus, google, developers, google developers, flutter codelab, asynchronous coding, async coding, GDS: Yes;
Id: TF-TBsgIErY
Channel Id: undefined
Length: 6min 53sec (413 seconds)
Published: Fri Jul 19 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.