What is the difference between async/await and Future?

High PriorityAsked in ~90% of Flutter interviews

4 min read

Dart Fundamentals

Future<T> and async/await are not alternatives — they work together. Future<T> is the object (a promise of a value that arrives later); async/await is the syntax sugar that lets you read async code top-to-bottom as if it were synchronous. Every async function returns a Future under the hood.

ConceptWhat it isMental model
Future<T>The return type — an object that will eventually hold a T or an errorA receipt you can redeem later
asyncMarks a function so it can use await and auto-wraps its return in a Future"This function does async work"
awaitPauses the current async function until the Future completes, then unwraps the value"Wait here, then continue"

Important: await doesn't block the thread. Dart is single-threaded; it yields control back to the event loop so the UI keeps responding.


Code in action

// The Future is the object you receive
Future<String> fetchUserName() {
  return Future.delayed(const Duration(seconds: 2), () => 'Alice');
}

// Style A: .then() — chained callbacks
void mainCallback() {
  fetchUserName()
      .then((name) => print('Hello, $name'))
      .catchError((e) => print('Error: $e'));
}

// Style B: async/await — same logic, reads top-to-bottom
Future<void> mainAwait() async {
  try {
    final name = await fetchUserName();   // pauses here, doesn't block UI
    print('Hello, $name');
  } catch (e) {
    print('Error: $e');
  }
}

Both do the exact same thing. async/await is just sugar over .then().


Sequential vs parallel — the perf trap

// 🐢 Sequential — 5s total
Future<void> slow() async {
  final user  = await fetchUser();   // 2s
  final posts = await fetchPosts();  // 3s, only starts AFTER user finishes
}

// 🚀 Parallel — 3s total (they run together)
Future<void> fast() async {
  final results = await Future.wait([fetchUser(), fetchPosts()]);
  final user = results[0], posts = results[1];
}

// ✨ Even cleaner with Dart 3 records
Future<void> fastest() async {
  final (user, posts) = await (fetchUser(), fetchPosts()).wait;
}

Rule: if two awaits don't depend on each other, you're leaving speed on the table.


When to use which style

SituationPick
Reading/writing a sequence of async stepsasync/await
Firing off async work from a non-async context (constructors, initState, event handlers)Return the Future or use .then()
Running independent async work in parallelFuture.wait / record .wait
Need a fallback if a Future is slow.timeout(...)
Want whichever finishes firstFuture.any([...])

Common mistakes to avoid

// ❌ Forgetting await — fire and forget, errors get swallowed
Future<void> save() async {
  saveToDb();           // ⚠️ returns a Future nobody is watching
  print('Saved!');      // prints before save actually happens
}

// ✅ Await it (or explicitly mark as intentional with `unawaited(...)`)
Future<void> saveOk() async {
  await saveToDb();
  print('Saved!');
}

// ❌ Awaiting things that could run in parallel
final a = await fetchA();
final b = await fetchB();   // wasted seconds if b doesn't depend on a

// ✅ Parallelise
final (a, b) = await (fetchA(), fetchB()).wait;

// ❌ Thinking async = multi-threaded
// Dart runs on a single thread. async/await yields to the event loop,
// it does NOT run on a background thread. For CPU-heavy work use Isolate.

// ❌ Marking a function async with no await — pointless wrapping
Future<int> answer() async => 42;     // wraps 42 in an unnecessary Future
int answerSync() => 42;               // do this instead

Interview follow-ups

  1. What happens if you forget await? The Future runs in the background, but you lose the result, can't catch errors (they become uncaught async errors), and your code continues past it immediately. Most teams enable the unawaited_futures lint to catch this.

  2. Does async run code on a separate thread? No. Dart is single-threaded with an event loop. await yields to the loop, letting other events (UI frames, taps, timers) run, but the work itself is on the same isolate. For real parallelism / CPU-heavy work, use Isolate.run(...) or compute(...).

  3. What's the difference between returning a Future from a sync function vs an async function? Future<T> foo() => bar(); returns whatever bar() returns directly. Future<T> foo() async => await bar(); wraps the result in a new Future and adds a microtask hop. The sync version is slightly faster; the async version gives you try/catch and await inside.

  4. When would you ever still use .then() over await? When you're not in an async function and can't make it one — constructors, initState, top-level callbacks. Also handy for fire-and-forget logging where you genuinely don't want to wait.

How helpful was this content?

Please sign in to rate this article.