♪ (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) ♪