Explain Dart's async/await, Future, and the microtask queue
TL;DR: Dart is single-threaded with an event loop. async/await syntactic sugar wraps code in Future continuations. Microtasks (scheduleMicrotask, Future.value) run before the next event; regular async operations (I/O, timers) are events.
Full Answer
Dart Event Loop
Dart runs on a single thread with two queues: the event queue (I/O, timers, user events) and the microtask queue. The event loop always drains the microtask queue completely before taking the next event.
Microtask vs Event queue
- ▸Microtask queue: Future.value(), Future.sync(), scheduleMicrotask() — run before next event
- ▸Event queue: I/O callbacks, Timer, network responses — run as events
- ▸await on a completed Future schedules the continuation as a microtask
- ▸await on a pending Future (e.g., HTTP request) schedules continuation as an event when it completes
This is why long synchronous loops block the UI in Flutter — they don't yield to the event loop. Use compute() or Isolates for CPU-intensive work.
Code Examples
1 — sync start 2 — sync end 2.5 — explicit microtask 3 — microtask 4 — event (Timer-like)
Interview Tip
Key insight: await suspension points are microtasks when the Future is already complete. This is why multiple awaits in a row can still block the UI if they all complete synchronously — use Future.delayed(Duration.zero) to yield to the event loop.