Dart
async/await
concurrency
asynchronous programming
troubleshooting

DART async/await not waiting

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

When await in Dart seems to be "not waiting," the real issue is usually that the calling code never awaited the returned Future, or that the work inside the async function started without being chained correctly. await pauses only the current async function, not the entire program.

What await Actually Does

In Dart, await can be used only inside an async function. It suspends that function until the awaited Future completes, then resumes execution with the result.

That means two things are true at the same time:

  • the current async function pauses at the await
  • the event loop continues running other work

So if code outside that function continues immediately, that is normal. await is not a global stop button for the isolate.

A Common Mistake

The most common bug is calling an async function without awaiting it.

dart
1Future<void> loadData() async {
2  await Future.delayed(Duration(seconds: 1));
3  print('data loaded');
4}
5
6void main() {
7  loadData();
8  print('done');
9}

This prints done before data loaded because main does not wait for the Future returned by loadData.

The Correct Version

Make the caller async too, and await the function.

dart
1Future<void> loadData() async {
2  await Future.delayed(Duration(seconds: 1));
3  print('data loaded');
4}
5
6Future<void> main() async {
7  await loadData();
8  print('done');
9}

Now the output order matches the dependency between the operations.

Another Source of Confusion: Mixing then and await

Dart allows both await and callback-based chaining with then, but mixing them carelessly makes execution order harder to follow.

dart
1Future<void> runTask() async {
2  Future.delayed(Duration(seconds: 1)).then((_) {
3    print('inside then');
4  });
5
6  print('after scheduling');
7}

This prints after scheduling first because the then callback was registered, not awaited. If you want sequential behavior, await the delayed future directly.

dart
1Future<void> runTask() async {
2  await Future.delayed(Duration(seconds: 1));
3  print('after wait');
4}

Returning the Right Future

Sometimes an async function starts another async operation but does not return or await it. That makes the outer function complete too early.

dart
1Future<void> saveAll() async {
2  Future.delayed(Duration(seconds: 1), () {
3    print('saved');
4  });
5}

Even though saveAll is async, it does not await the delayed work, so callers cannot rely on it.

The fix is:

dart
1Future<void> saveAll() async {
2  await Future.delayed(Duration(seconds: 1), () {
3    print('saved');
4  });
5}

Now the returned Future represents the actual completion of the work.

Debugging Execution Order

When troubleshooting async order, add timestamps or simple prints at every boundary:

  • before the async call
  • immediately after the call
  • inside the awaited operation
  • after the await

That usually reveals whether the problem is a missing await, an unreturned Future, or just a misunderstanding of isolate scheduling.

Common Pitfalls

  • Calling an async function without await is the most common reason Dart appears not to wait. The returned Future must be awaited or returned by the caller.
  • Assuming await pauses the whole application leads to incorrect expectations. It pauses only the current async function.
  • Mixing then callbacks and await without a clear reason makes control flow harder to follow. Prefer one style per code path.
  • Starting async work inside an async function without awaiting or returning it causes the outer Future to complete too early. Make the function represent the real completion point.
  • Ignoring the caller chain leaves the top-level workflow effectively asynchronous even if an inner function uses await correctly. Async correctness has to propagate upward.

Summary

  • In Dart, await pauses the current async function, not the whole isolate.
  • If code seems to continue too early, the caller often forgot to await the returned Future.
  • Mixing then and await can hide the real execution order.
  • An async function must await or return all relevant inner futures.
  • Most await not waiting bugs are really Future propagation bugs.

Course illustration
Course illustration

All Rights Reserved.