Skip to main content

Menu

System onlineAI Tutorials / 2026

AI Tutorials
Knowledge search

Recent · Popular · Tutorials

  • dart3 tutorials
  • java1 tutorial
  • Javascript3 tutorials
  • Node.js Introduction2 tutorials
  • dartdart · General · In depth
  • JavascriptJavascript · Advanced · In depth
  • JavascriptJavascript · Advanced · Mid
  • JavascriptJavascript · Advanced · Short

Find a tutorial

Tell the assistant what you want to learn. It suggests published chapters.

For example: “I want to learn Rust ownership from the start.” The conversation is kept only in this tab.

0 / 2,000 characters

Enter to send, Shift+Enter for a new line. Do not include personal information.

Chapter 16

Futures, async, and await

This chapter assumes you have read the earlier chapters on functions and on exceptions, and that you can run a Dart program from the command line. Here we look at how Dart handles work that takes time, like downloading a file or waiting for a timer.
Level: GeneralLength: In depth4 min read

Read at another depth

Futures, async, and await

This chapter assumes you have read the earlier chapters on functions and on exceptions, and that you can run a Dart program from the command line. Here we look at how Dart handles work that takes time, like downloading a file or waiting for a timer.

Why waiting is a problem

Some work takes a long time. Reading a file from disk takes a few thousandths of a second. Downloading a web page can take a full second. Fetching data over a slow phone connection can take many seconds.

A program normally runs one line at a time. If a line says "download this page," the program stops and waits until the download finishes. During that wait, it cannot respond to clicks, draw animations, or do anything else. To a person using the program, it looks frozen.

Dart solves this with a simple idea: start the slow work, and promise to come back with a result later. Keep doing other things while you wait. That promise has a name in Dart: a Future.

What is a Future?

A Future is an object that stands for a value that does not exist yet. It is a placeholder. When the slow work finishes, the Future is "completed" with the value. If something goes wrong, the Future is completed with an error instead.

The type name tells you what value is coming. Future<int> means "an int, eventually." Future<String> means "a String, eventually." A Future with no value at all, for work that just finishes without producing anything, is Future<void>.

You never create a plain Future yourself to fake a value. Instead, functions that do slow work return Futures. You write code that waits for the Future to complete.

async and await

Two keywords make waiting readable.

  • async marks a function as one that can wait for Futures. It goes after the parameter list.
  • await means "pause here until this Future completes, then give me its value." It can only be used inside an async function.

Here is a small complete program:

Code example in dart
Future<String> fetchMessage() async {
  await Future.delayed(Duration(seconds: 1));
  return 'done';
}

Future<void> main() async {
  try {
    final message = await fetchMessage();
    print(message);
  } catch (e) {
    print('failed: $e');
  }
}

Future.delayed returns a Future that completes after the given duration. It stands in for any slow task, like a network call. The line await Future.delayed(...) pauses this function for one second, then moves on.

Notice two details:

  1. main itself is async and returns Future<void>. Any function that uses await must be async.
  2. We wrapped the await in try and catch. If the slow work fails, the error appears right there, in the same style as ordinary error handling from the exceptions chapter. This is one of the nicest parts of async and await: waiting code reads like ordinary code, top to bottom.

What happens while you wait

When a function hits an await, it does not block the whole program. It hands control back and lets Dart run other pending work, such as handling a button press or a timer. When the awaited Future completes, Dart resumes your function from that exact line.

This single-threaded style is why Dart never has the classic problem of two code sections modifying the same variable "at the same time." Only one piece of your code runs at any instant. Waiting is cheap; true parallel work is a different tool (covered in the isolates chapter).

Building slow work from scratch

You can turn any slow job into a Future-returning function with async:

Code example in dart
Future<int> slowDouble(int n) async {
  await Future.delayed(Duration(milliseconds: 500));
  return n * 2;
}

Future<void> main() async {
  print('start');
  final result = await slowDouble(21);
  print(result); // 42
}

The function looks normal, but calling it returns a Future immediately. The body runs later, piece by piece, pausing at each await.

Waiting for several things

A common mistake is waiting for things one at a time when they could happen at once:

Code example in dart
// Slow: waits 1 second, then another 1 second. About 2 seconds total.
final a = await slowDouble(1);
final b = await slowDouble(2);

// Fast: starts both, then waits for both. About 1 second total.
final results = await Future.wait([slowDouble(1), slowDouble(2)]);
print(results); // [2, 4]

Future.wait takes a list of Futures and completes when all of them have completed, giving you a list of results in the same order. If any of them fails, Future.wait fails too.

Call this out explicitly:

Code example in dartcallout note
Put `await` as late as possible. Write `final fa = slowDouble(1); final fb = slowDouble(2);` first, then await them. Starting the work and waiting for it are separate steps, and separating them lets independent tasks overlap.

Without await: then and catchError

Before async and await, Dart code used a method called then. It hands the Future a function to run when the value arrives:

Code example in dart
slowDouble(21).then((value) => print(value));

You will still see then in older code and in some libraries. It works, but chains of then calls get hard to read. Prefer await. The one place then is still natural is a quick one-line reaction where you do not want to make the surrounding function async.

Common mistakes

  • Forgetting async. Using await in a function without the async keyword is a compile-time error. The fix is simple, but the error message can be confusing at first.
  • Calling an async function and ignoring the returned Future. print(slowDouble(21)) prints something like Instance of 'Future<int>', not a number. You must await it.
  • Awaiting in a loop when tasks are independent. A loop of await calls runs the slow tasks one after another. Use Future.wait when they can overlap.
  • Not handling errors. An error inside an async function becomes a failed Future. If nobody awaits it and nobody catches the error, the failure disappears silently. Always await your Futures or attach a .catchError.

Practice

Code example in dartexercise
Q: You need to download two files, A and B. File A takes 3 seconds, file B takes 4 seconds. If you await them one after the other, how long does the total wait take, and how long does it take if you use Future.wait?
A: One after the other: 3 + 4 = 7 seconds, because the second download does not start until the first finishes. With Future.wait, both downloads start at once and finish independently, so the total is about 4 seconds, the longer of the two.
Code example in dartexercise
Q: What is wrong with this code, and how do you fix it?
void main() {
  final result = fetchMessage();
  print(result);
}
A: fetchMessage returns a Future<String>, not a String, so the code prints the Future object rather than the message. Also, using await requires an async function. Fix: declare main as Future<void> main() async and write final result = await fetchMessage();.

Spotted a mistake, or code that does not work?

Report an issue

Chapter: Futures, async, and await

0 / 2,000 characters

Plain text, for example: the second code sample does not compile. Do not include personal information.

You can't do that