[MUSIC PLAYING] ANDREW BROGDON: Hey, everybody. I'm Andrew from
the Flutter team, and welcome to the
"Flutter in Focus" miniseries on a asynchronous
programming in Dart. This is the first in a run of
videos covering the ways Dart, despite being a
single-threaded language, offers support for
futures, streams, background work, and
all the other things you need to write in a
modern, asynchronous, and, in the case of
Flutter, reactive way. Since this is the first
video in the series, I'm going to start
all the way down at the foundation of what makes
asynchrony possible with Dart, and that's the isolate. An isolate is what
all Dart code runs in. It's like a little
space on the machine with its own private
chunk of memory and a single thread
running an event loop. In a lot of other
languages like C++, you could have multiple
threads sharing the same memory and running whatever
code they want. In Dart, though, each
thread is in its own isolate with its own memory and
it just processes events. More on that in a minute. Many Dart apps run all their
code in a single isolate, but you can have more
than one if you need it. If you have a computation to
perform that's so enormous it could cause you to drop frames
if it were run in the main isolate, you can
use Isolate.spawn() or Flutter's compute function,
both of which create a separate isolate to do the
number crunching, leaving your main one free to
rebuild and render the widget tree in the meantime. That new isolate will
get its own event loop and its own memory, which
the original isolate, even though it's the parent
of this new one, isn't allowed to access. That's the source
of the name isolate. These little spaces are kept
isolated from one another. In fact, the only way
they can work together is by passing messages
back and forth. One isolate will send
a message to the other, and that receiving
isolate process is the message using
its event loop. This lack of shared memory
may sound kind of strict, especially if you're coming from
a language like Java or C++, but it has some key
benefits for Dart coders. For example, memory allocation
and garbage collection in an isolate don't
require locking. There's only one thread,
so if it's not busy, you know the memory's
not being mutated. That works out really well for
Flutter apps, which sometimes need to build up and tear
down a bunch of widgets really quickly. All right. So that's a basic
introduction to isolates. Now let's dig into what really
makes async code possible, the event loop. Imagine the life of an app
stretched out on a timeline. Here you start, there you
stop, and in between, there are all these little events,
like I/O from the disk, or finger taps from the
user-- all kinds of stuff. Your app can't predict when
these events will happen or in what order, and it
has to handle all of them with a single thread
that never blocks, so it runs an event loop. Simple as can be. It grabs the oldest event
from the event queue, processes it, goes back for the
next one, processes that one, and so on until the
event queue is empty. The whole time the
app is running-- you're tapping on the screen,
things are downloading, a timer goes off-- that event loop is just
going around and around, processing those
events one at a time. Whenever there's a
break in the action, the thread just
kind of hangs out, waiting for the next event. It can trigger the garbage
collector, get some coffee, whatever. All of the high level APIs
we're used to for asynchronous programming-- futures,
streams, async and await-- they're all built on and
around this simple loop. For example, say you have a
button that initiates a network request, like this one. You run your app and
Flutter builds the button and puts it on
screen, then it waits. The event loop
just sort of idles, waiting for the next
thing to process. Other events not
related to the button might come in and get
handled while the button just sits there waiting for
the user to tap on it. Eventually they do, and a
tap event enters the queue. That event gets picked
up for processing, Flutter looks at it, and
the rendering system says, hey, those coordinates
match the RaisedButton, so Flutter executes
the onPressed function. That code initiates a network
request, which returns a future and registers a completion
handler for the future by using then. And that's it. The loop is finished
processing that tap event and it's discarded. Now, onPressed was a
property on RaisedButton, and here we're talking about
a callback for a future. But both of those techniques are
doing basically the same thing. They're both a way to tell
Flutter, hey, later on, you might see a particular
type of event come in. When you do, please
execute this piece of code. onPressed is waiting
for a tap and the future is waiting for network data. But from Dart's
perspective, those are both just
events in the queue. And that's how a synchronous
coding works in Dart. futures, streams,
async, and await-- these APIs are all just ways for
you to tell Dart's event loop, here's some code. Please run it later. If we look back at
the code example, you can now see exactly how
it's broken up into blocks for particular events. There's the initial
build, the tap event, and the network response event. Once you get used to
working with async code, you'll start recognizing these
patterns all over the place. And understanding
the event loop is going to help as we move on
to the higher level APIs. All right. So that's a quick look at
isolates, the event loop, and the foundation of
async coding in Dart. In our next video, we're
going to talk about futures, a simple API you can use to take
advantage of these capabilities without a ton of code. In the meantime, leave a comment
below if you have a question, and come see us at flutter.io. [MUSIC PLAYING] 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.
Awesome quality, I am excited for this series
Nice intro. ๐๐๐