Async/Await - Flutter in Focus

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
♪ (music) ♪ Hey everybody, and welcome to the fourth video in our <i>Flutter in Focus</i> series on asynchronous coding in Dart. In this episode, I'm going to show you how to use Dart's <i>async</i> and <i>await</i> keywords to simplify your code. A lot of languages have <i>async</i> and <i>await</i> in their syntax, and the first time I saw them I remember being weirded out. I knew you tagged a function as <i>async,</i> the return type changed, and somewhere in the middle there was a break where it would wait. But at the time it just seemed magical and weird. I have some good news for you all watching this, though. If you've seen the first few videos in this series, you already know the things you need to fully understand <i>async</i> and <i>await.</i> And that's because, at the end of the day, they're really just an alternate syntax for using futures and streams that can help you write cleaner, more readable code. Let's start with a simple example. Say I have a class that represents some processed data. I can give it a string and it'll do some business logic that I need done. I also have a method that'll load an id value from disk and another that'll fetch some network data that I can use with my class. Network and File I/O are asynchronous operations, so they return futures. I'd like to write a single method that will put these pieces together. First, it should load an id from disk, then use that id to make a network call, then make a <i>ProcessedData</i> object with the result. With Dart's futures API, I can use <i>then</i> to chain callbacks together so that the completed value of the first future becomes the parameter for the next callback, and so on. This technique was covered earlier in the series, and it works great. The code isn't as readable as it would be if this were all synchronous, though. If it weren't for the futures, that code could be written like this, right? Make some calls, in order, no big whoop. Well, the big deal about <i>async</i> and <i>await</i> is that you can have code that looks like this, <i>and</i> uses futures. First, add the <i>async</i> keyword, just before the opening brace. This is just a way of telling Dart, "Hey! I plan to use the <i>await</i> keyword in here." Speaking of which, next up is placing the <i>await</i> keyword in front of each future the function needs to wait for. It can't call <i>fetchNetworkData</i> without the id from <i>loadFromDisk,</i> so there needs to be an <i>await</i> there. And it can't create a <i>ProcessedData</i> object without the data from the network. So, there needs to be an <i>await</i> there. The last change is to make the return type a future. That might look weird at first, because the return statement on this function just uses a regular <i>ProcessedData</i> object. But before <i>createData</i> here completes, it has to wait on two futures. That means it's going to start executing, then stop, and wait for a disk event, then keep going, then stop and wait for a network event. And only after that can it provide a value. So when <i>createData</i> starts running and hits that first await, right then it returns a future to the calling function. It says, "Hey! Looks like I'm going to have to wait on some stuff. Here's this empty box, you hold onto that, and when I'm done waiting for this disk in the network, I'll call the return statement and put some data in there for you. Go put it in a <i>FutureBuilder</i> or something." And that's how this works. Before moving on, let's take a quick look back at the event loop, and how it works with both versions of the code you just saw. We started with this one which uses the futures API. And one of the nice things about the futures API is that you can easily see how the code is broken down for the events involved. First, the function starts running and calls in to <i>loadFromDisk.</i> Then it waits. It waits for some data from the disk to arrive at the event loop. That completes the future returned by <i>loadFromDisk.</i> So, the callback from the first event statement is invoked and a network request goes out. Then <i>createData</i> waits again. It waits for some network data to arrive at the event loop. That completes the future returned by <i>fetchNetworkData,</i> so the second callback is invoked, some process data is created, and now the future that was returned by this <i>createData</i> is completed with that value. Now, let's do the same thing again, using the <i>async/await</i> version of the code. Spoiler Alert! It's the exact same process. Before, we could use the calls to <i>then</i> to imagine how the code is broken down, event by event. Here, you can do the same thing by breaking the code after each <i>await</i> expression. So we get this nice line-by-line progression. Let's run it. <i>createData</i> starts executing and hits that first <i>await.</i> At that point, it returns its own future to the calling function, and invokes <i>loadFromDisk.</i> Then, just like before, it waits for that File I/O event from the disk. That completes the future returned by <i>loadFromDisk,</i> which means <i>createData</i> is done awaiting on it and can go on to the rest of the code. Next, it calls <i>fetchNetworkData,</i> and waits again. Eventually, the network data arrives at the event loop, that completes the future return by <i>fetchNetworkData,</i> and so <i>createData</i> is free to move on again. It creates and returns an instance of <i>ProcessedData,</i> which completes the future that <i>createData</i> gave to its caller way back at the beginning. As you can see, in both cases, the same event loop controls the action, and the same futures are involved. The only real change is that with <i>async/await</i> the function is smaller and looks more like synchronous code. Hopefully, at this point, some of you are thinking, "Hey, I watched a couple of the other videos in this series and you said futures could complete either with data or an error. What's up with <i>async/await</i> and errors?" The answer is that <i>async</i> and <i>await</i> also help make your error handling look more like what it would be with synchronous code. If we go back to that first example based on the futures API, error handling code might look like this. It uses <i>catchError</i> to test and respond to errors, and, when complete, to execute a callback at the very end, whether there's an error or not. With <i>async</i> and <i>await,</i> on the other hand, rather than using additional callbacks, you can use <i>try-catch.</i> Inside a function tagged with the <i>async</i> keyword, <i>try-catch</i> blocks will handle asynchronous errors the same way they handle synchronous ones in normal functions. You can use the <i>on</i> and <i>catch</i> keywords to trap specific types of exceptions and <i>finally</i> will execute its code block at the end as you would expect. Okay, there's one last thing to cover, and that's how to use <i>await</i> with a <i>for</i> loop to process data from a stream. This is a much less common use case for the <i>await</i> keyword, but it is something you can do. Say I have a function that can take in a list of numbers and add them all up. That's pretty straightforward, right? I can just use a <i>for</i> loop to iterate over the values. What if I wanted this function to take a stream of numbers instead, add them up asynchronously as they arrive, and then, when the stream is finished, return that sum? Just like with futures, <i>async/await </i>helps me make that happen without changing the basic structure of the code. First, I tag the function as <i>async,</i> then I change the return type to a future, and then I add the <i>await</i> keyword in front of <i>for,</i> and I'm done! Just like with futures, the <i>await</i> keyword is separating my function into the parts that execute before and after waiting on events. First, it starts executing and gets all the way to that <i>await.</i> Then it returns its future to the calling function and waits for a piece of data to arrive. When it does, the loop executes once to process that piece of data and then stops and waits for the next one. Maybe the app runs off and does some other things, garbage collects, whatever. Eventually, though, another piece of data arrives and the loop goes around again. This keeps happening until the stream is finished and closes. When that happens, the function exits the loop and executes its return statement. That completes the future that <i>getTotal</i> here gave to its caller way back at the beginning. One important thing to keep in mind when using <i>await for</i> is that you should only use it with streams you know are going to complete. If you try to use this with a stream of clicks coming from an HTML button, for example, that stream lasts as long as the button's around, which means your loop could just keep right on waiting. Alright. That's all we have for this video, but there's one more left in the series, and we'll be talking about generator functions. These are functions that can return multiple values over time, creating a stream of data on the fly. So, be on the lookout for that, and head to <i>dart.dev</i> and <i>flutter.dev</i> for more info on Dart and Flutter. ♪ (music) ♪ Hey! If you enjoyed that video, try these others. Or, subscribe to the Flutter channel. It's Google's new portable UI toolkit. There's a button around here somewhere. ♪ (music) ♪
Info
Channel: Flutter
Views: 134,278
Rating: 4.9622416 out of 5
Keywords: Dart streams, Dart async, Dart isolates, Asynchronous dart, Dart stream API, Dart, building widgets, widgets, simplify your code, async, await, Flutter, Flutter In Focus, synchronous code, developers, dart language, google, stateless widgets, stateful widgets, GDS: Yes;
Id: SmTCmDMi4BY
Channel Id: undefined
Length: 9min 11sec (551 seconds)
Published: Fri Jul 12 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.