flutter
/

Dart Async/Await – Asynchronous Programming Made Easy

Last Sync: Today

On this page

10
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

flutter

Dart Async/Await – Asynchronous Programming Made Easy

What is Asynchronous Programming?

Asynchronous programming allows your program to perform time‑consuming tasks (like network requests, file I/O, or database queries) without blocking the main thread. Instead of waiting for the task to complete, the program continues executing other code and gets notified when the task is done. This is essential for building responsive applications, especially in Flutter.

Futures: The Building Block

A Future represents a potential value or error that will be available at some time in the future. You can think of it as a promise that a value will be delivered later. Dart provides two ways to work with Futures: using the .then() callback or using async/await. The latter is more readable and recommended.

DARTRead-only
1
void main() {
  // Simulate a network request
  Future<String> fetchData() {
    return Future.delayed(Duration(seconds: 2), () => 'Data loaded');
  }

  // Using .then()
  fetchData().then((data) {
    print(data);
  });

  print('Waiting...');
}

The async and await Keywords

The async keyword marks a function as asynchronous. It tells Dart that the function will return a Future. Inside an async function, you can use await to pause execution until a Future completes, without blocking the thread. The await expression returns the value of the Future (or throws an error if the Future completes with an error).

DARTRead-only
1
Future<String> fetchData() async {
  await Future.delayed(Duration(seconds: 2));
  return 'Data loaded';
}

void main() async {
  print('Start');
  String data = await fetchData();
  print(data);
  print('End');
}
// Output:
// Start
// (2 second pause)
// Data loaded
// End

Note that main() itself can be async. This is common in command‑line apps and Flutter's main() can also be async if needed.

Error Handling with try/catch

When you use await, errors from the Future are thrown as exceptions. You can catch them using a regular try/catch block, just like synchronous code.

DARTRead-only
1
Future<String> fetchData() async {
  await Future.delayed(Duration(seconds: 1));
  throw Exception('Network error');
}

void main() async {
  try {
    String data = await fetchData();
    print(data);
  } catch (e) {
    print('Caught error: $e');
  }
}

Multiple Awaits and Sequential Execution

When you need to perform several asynchronous tasks one after another, you can simply await each one in order. They will run sequentially.

DARTRead-only
1
Future<int> fetchUserId() async {
  await Future.delayed(Duration(seconds: 1));
  return 42;
}

Future<String> fetchUserData(int id) async {
  await Future.delayed(Duration(seconds: 1));
  return 'User data for id $id';
}

void main() async {
  int id = await fetchUserId();
  String data = await fetchUserData(id);
  print(data); // takes ~2 seconds total
}

Running Futures in Parallel: Future.wait

If you have multiple independent asynchronous tasks, you can run them concurrently using Future.wait. It takes a list of Futures and returns a Future that completes when all of them are done, yielding a list of results.

DARTRead-only
1
Future<String> fetchFirst() async {
  await Future.delayed(Duration(seconds: 2));
  return 'First';
}

Future<String> fetchSecond() async {
  await Future.delayed(Duration(seconds: 1));
  return 'Second';
}

void main() async {
  var results = await Future.wait([fetchFirst(), fetchSecond()]);
  print(results); // ['First', 'Second'] (takes ~2 seconds, not 3)
}

Async* and Streams (Brief Overview)

For multiple values over time, Dart uses Stream. An async* function returns a Stream and uses yield to emit values. This is beyond the scope of async/await but is another important asynchronous concept.

Best Practices

    • Always mark functions that use await with async.
    • Prefer async/await over .then() for readability.
    • Use try/catch to handle errors in async functions.
    • Use Future.wait for concurrent independent tasks.
    • Be careful with long‑running synchronous code inside an async function; it still blocks the event loop.

Complete Example

DARTRead-only
1
Future<String> fetchUser(int id) async {
  // Simulate network delay
  await Future.delayed(Duration(seconds: 1));
  if (id <= 0) throw Exception('Invalid user ID');
  return 'User$id';
}

Future<void> main() async {
  print('Fetching users...');

  try {
    // Sequential
    var user1 = await fetchUser(1);
    print(user1);

    // Parallel
    var ids = [2, 3, 4];
    var futures = ids.map((id) => fetchUser(id));
    var users = await Future.wait(futures);
    print(users);

    // Error case
    var badUser = await fetchUser(-1);
    print(badUser);
  } catch (e) {
    print('Error: $e');
  }
}

Key Takeaways

    • async marks a function that returns a Future.
    • await pauses the function until a Future completes.
    • Use try/catch to handle errors in async code.
    • Future.wait runs multiple Futures concurrently.
    • Async/await makes asynchronous code look and behave like synchronous code, improving readability.

Try it yourself

Future<String> fetchData() async {
  await Future.delayed(Duration(seconds: 2));
  return 'Hello, async world!';
}

void main() async {
  print('Fetching...');
  String data = await fetchData();
  print(data);
}

Test Your Knowledge

Q1
of 4

What keyword is used to mark a function as asynchronous in Dart?

A
await
B
async
C
future
D
defer
Q2
of 4

What does `await` do?

A
Pauses the entire program until a Future completes
B
Pauses the current function until a Future completes, without blocking the event loop
C
Starts a Future and continues immediately
D
Throws an error if the Future fails
Q3
of 4

How do you run multiple independent Futures concurrently and wait for all of them?

A
await Future.all()
B
await Future.wait([...])
C
await Future.any([...])
D
Use a for loop with await
Q4
of 4

What is the return type of an async function that returns a string?

A
String
B
Future<String>
C
Future
D
String?

Frequently Asked Questions

What is the difference between `async` and `async*`?

async is used for functions that return a single value wrapped in a Future. async* is used for functions that return a stream of values (Stream) and uses yield to emit each value.

Can I use `await` outside an `async` function?

No, await can only be used inside a function marked async. This includes the top‑level of a Dart program – you cannot use await directly in main() unless main() is marked async.

What happens if I forget `await` on a Future?

The Future will start running, but you won't wait for its result. The code will continue immediately, and you'll have no way to access the value or handle errors unless you use .then().

How do I handle multiple dependent async calls?

You can await each one sequentially, or if they can run in parallel but you need all results before proceeding, use Future.wait.

Can I use `async`/`await` with `Stream`?

Yes, you can use await for to iterate over a stream asynchronously. For example: await for (var value in myStream) { ... }.

Does `await` block the main thread?

No, await does not block the thread. It tells the function to suspend execution and lets the event loop handle other tasks. When the Future completes, the function resumes from where it left off.

Previous

dart closures

Next

dart future

Related Content

Need help?

Explore our comprehensive docs or start a chat with our tech experts.