Behaviour of Future.delayed in dart
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Future.delayed schedules work to complete later, but "later" in Dart has precise event-loop semantics. Most confusion comes from mixing it up with microtasks, assuming Duration.zero means immediate execution, or forgetting that await pauses only the current async function rather than the whole isolate.
What Future.delayed Actually Does
Future.delayed creates a future that completes after a timer fires. If you pass a callback, the future completes with that callback's result.
This does not block the isolate. The event loop is free to process other events while the timer is waiting.
You can also return a value:
The future completes with 42 after the delay.
Duration.zero Is Still Asynchronous
A common misunderstanding is that Future.delayed(Duration.zero, ...) runs immediately. It does not. It schedules the callback for a later event-loop turn.
Output order is:
So Duration.zero means "schedule soon on the event queue," not "run inline."
Difference from Microtasks
Dart has both the event queue and the microtask queue. Microtasks run before the next event is processed. Future.delayed uses the event queue because it is timer-based.
Typical order:
This matters in Flutter because microtasks can run before the next frame-related event, while delayed futures wait for the event queue turn.
await and Control Flow
When you await a delayed future, only the current async function pauses.
The call to worker() starts execution, reaches the await, and returns control to the event loop. main continues immediately.
That is why Future.delayed is good for timers, retries, and staged UI work, but not for blocking behavior.
Common Flutter Usage
In Flutter, Future.delayed is often used for:
- splash screen transitions
- debounced actions
- temporary banners
- retry backoff
Simple example:
Be careful when delayed callbacks capture a widget state object. If the widget is disposed before the callback runs, updating UI can throw or produce stale behavior.
That mounted check is the right habit for delayed UI work inside a State object.
Error Behavior
If the callback passed to Future.delayed throws, the future completes with an error.
This is normal future behavior. You handle it with await and try/catch, or with .catchError.
When Not to Use It
Do not use Future.delayed as a substitute for proper lifecycle or scheduling APIs. In Flutter, if the real goal is "after the first frame," a post-frame callback is often more accurate than an arbitrary delay.
Likewise, avoid magic delays to "wait for state to settle." If correctness depends on exact state transitions, use explicit signals, not guessed timings.
Common Pitfalls
- Assuming
Duration.zeromeans synchronous execution. - Confusing event-queue delayed futures with microtasks.
- Updating Flutter state after a delayed callback without checking
mounted. - Using arbitrary delays instead of lifecycle-aware APIs.
- Expecting
await Future.delayed(...)to block the whole program.
Summary
- '
Future.delayedcompletes a future after a timer fires.' - Even
Duration.zeroruns asynchronously on the event queue. - It is different from microtasks, which run earlier.
- '
awaitpauses only the current async function, not the isolate.' - In Flutter, delayed UI callbacks should respect widget lifecycle.

