An Introduction to Dart’s Futures

Nikolay Miroshnychenko
6 min readAug 25, 2020

Asynchronous operations are a core part of any programming language and play a huge role in the development of modern mobile apps.

The Flutter team has put together a great mini-series on this topic. I highly recommend watching it.

What is the Future?

First of all, let’s understand what a Future is. From the docs:

A Future is used to represent a potential value, or error, that will be available at some time in the future.

In other words, a Future is an object that will at a certain point hold some value, but not currently. One example that you can think of it is like an oven that’s cooking something— an oven cooks something and eventually, you hear a “ding”. You look inside and what you wanted to cook is finally ready. The same thing goes for the Future — until you hear that “ding” your Future is in an uncompleted state. Once the “ding” goes off — your Future is completed.

Let’s build a simple Future that just prints a message and returns a number after a delay:

Go ahead and run the code. As you can see we get the print statement in the following order:

print statement before invoking the future 
print statement after invoking the future
Future completed!

What happened here is we created a Future that completed with a delay and returned a value. This ()=> print(‘Future completed!) is a callback that we received once it completed.

Now, Dart is a single-threaded language, so how do we do this asynchronous call you might ask? This is possible due to something called an Event Loop. Understanding how the Event Loop works will help you put together a clear picture of how the Futures are handled “under the hood”.

Event Loop

An event loop is basically a mechanism that’s constantly running and processing whatever events we or the system throws at it. That mechanism is contained in something called an Isolate. Each Isolate has the following: 1) its own memory that it doesn’t share with any other Isolate (hence the name Isolate) 2) a single thread of execution 3) a single event loop. Unless you create other Isolates, all of your Dart code is going to be run inside a single main Isolate and your application is going to stay single-threaded (have a single Event Loop). I’m going to refer to the event loop as EL further down.

As mentioned before, the EL is constantly running, and it’s processing any incoming events. Since the EL has to do this work sequentially — it cannot process everything at once and therefore it puts events in a queue. Just like any other queue, it processes incoming events on a “first in-first out” basis and then discards them. Now, besides the Event queue, the EL has a Microtask queue as well, but for the sake of this article, we’re just going to stick with a simplified event queue model.

taken from https://dart.cn/articles/archive/event-loop

Now, let’s look at a bit of a more interactive example in order to understand how the EL processes the events.

First of all — this example has a then method being called on the Future that we’ve created. The then method is a convenience method that helps us register a callback for when our Future completes.

Now if we look into this example we can see that the first event is processed when the onPressed() method is invoked. When we press the button our EL receives the first event it needs to process, puts it in a queue, and eventually gets its hands on this event and processes it. The same process is repeated when the Future completes — the EL receives the event, schedules it, and processes it once it’s done with what was scheduled before it. One important thing to note here — the EL is free to do whatever it wants in between these events, i.e. it can process a network request event, handle another onPressed() event, etc.

The “await” keyword or making your asynchronous code look synchronous

Let’s look at this example. Try to guess what’s going to happen here before actually running it.

Looks like we’re gonna block the thread when we encounter the first await keyword, right? Wrong :) We’re not blocking anything here in fact. Our method _runTheFuture() immediately returns the Future, once it hits the await keyword. Think of the await keyword as something similar to a return statement but the execution is going to continue from the point of the await once our oven that bakes our cakes (or whatever you like) does the “ding”.

Now the same code, but with a regular old then invocation:

Now, this code does exactly the same thing as await_demo.dart, but with 1 difference. The code with the await returns an uncompleted Future once we hit the await — and we can see that once we get to this line:

print('myFuture.runtimeType: ${myFuture.runtimeType}');

and once we run it we get the following:

myFuture.runtimeType: _Future<dynamic>

Whereas if we run the code without the await we get the following result in the same line:

myFuture.runtimeType: Null

So basically they’re exactly the same thing. In the first code example with the await that can be useful if we then pass the Future to something called a FutureBuilder (which I’ll use in an example a little bit further down). Meanwhile, let’s cover error handling.

When your Future completes with an error

Returning to our oven analogy, just like you can burn your dish, you can have the same result with your Future — it can complete with an error.

Error handling is pretty simple with a Future. As you might know, you can throw any non-null value in Dart, so let’s look at these two examples.

You can catch an error in a Future with catchError() and you can use whenComplete() to execute something regardless of an error.

and if you want to do the same thing while using the await you do it via try/catch/finally blocks:

Error handling is pretty well covered in Dart’s docs — so check them out for a detailed error handling info.

So that’s pretty much for an intro to Dart’s Futures. Now let’s look into something that can simplify your life when working with Futures.

FutureBuilder or a simplified way to interact with the Future in your UI

So you have your Future returning what you want, you got your error handling in place, how do you interact now with the UI in a proper way?

Turns out the Flutter team has solved that for us as well, God bless their souls. There’s something called a FutureBuilder for that. What the FutureBuilder is from the docs:

Widget that builds itself based on the latest snapshot of interaction with a Future.

In other words, the FutureBuilder will trigger the build() method each time the Future gets a new value or an error. It will also rebuild the widget depending on the connectionState property of the AsyncSnapshot class. This allows us to track whether we’re still waiting for the Future to complete and show the proper UI accordingly. We can, of course, handle everything ourselves via setState() but why not use something that’s specifically built just for that? Let’s make use of the FutureBuilder in this final interactive example:

In this example, we alternate between throwing an error and completing the Future with a value. The FutureBuilder handles the result of our Future for us. All that we do is we display the LinearProgressIndicator depending on the connectionState property in order to track whether our Future completed yet or not.

Summary

Futures in Dart can be a bit confusing, but with a bit of practice and understanding how the Event Loop works and how the Futures interact with it — they’re easily tamed. This is an introductory coverage of Futures, so be sure to check out the various other features that they have here.

Flash cards and contacts

In order to refresh my understanding of various topics I usually build flashcards to test my knowledge.

Here are my Quizlet flashcards if you want to use them to test yourself later down the line ;)

Thanks for reading. If you found this post valuable, please recommend it (the little handclap) so it can reach others.

Also, if you have any feedback or anything that you want to share contact me in Telegram — https://t.me/@nsmirosh or via my e-mail — nsmirosh@gmail.com

--

--

Nikolay Miroshnychenko

Android engineer. Learning daily and sharing my knowledge in the process. Into mobile, computer science, and the brain.