Skip to content

Futures and Async/Await

A Future<T> in Dart is a Promise<T> in JavaScript. Both represent a single value that is not available yet — it will arrive (or fail) at some point in the future. Every concept you know from JS Promises maps directly: .then(), .catch(), async/await, try/catch, and parallel execution with Future.wait / Promise.all. The mental model is the same; only the API surface changes slightly.

React
// --- Promise chain style ---
function loadUser(id) {
return fetch(`/api/users/${id}`)
.then(res => res.json())
.then(data => {
console.log('User:', data.name);
return data;
})
.catch(err => {
console.error('Failed:', err.message);
});
}
// --- async/await style ---
async function loadUserAsync(id) {
try {
const res = await fetch(`/api/users/${id}`);
const data = await res.json();
console.log('User:', data.name);
return data;
} catch (err) {
console.error('Failed:', err.message);
}
}
Flutter
// --- Future chain style ---
Future<void> loadUser(String id) {
return Future.delayed(
const Duration(seconds: 1),
() => {'name': 'Ava', 'id': id},
)
.then((data) {
print('User: ${data['name']}');
})
.catchError((err) {
print('Failed: $err');
});
}
// --- async/await style ---
Future<void> loadUserAsync(String id) async {
try {
// Simulating an HTTP call with Future.delayed
final data = await Future.delayed(
const Duration(seconds: 1),
() => {'name': 'Ava', 'id': id},
);
print('User: ${data['name']}');
} catch (err) {
print('Failed: $err');
}
}

The app below demonstrates a FutureBuilder consuming a Future.delayed — the Flutter pattern equivalent to useEffect(() => { fetch(...).then(setData) }, []). Watch the spinner disappear and the result appear after two seconds.

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: Scaffold(
        body: Center(child: FutureDemo()),
      ),
    );
  }
}

// Simulates a network call — replace with http.get() in a real app.
Future<String> fetchMessage() {
  return Future.delayed(
    const Duration(seconds: 2),
    () => 'Hello from the future!',
  );
}

class FutureDemo extends StatefulWidget {
  const FutureDemo({super.key});

  @override
  State<FutureDemo> createState() => _FutureDemoState();
}

class _FutureDemoState extends State<FutureDemo> {
  // Hold the Future in state so it does not restart on every rebuild.
  late final Future<String> _messageFuture;

  @override
  void initState() {
    super.initState();
    _messageFuture = fetchMessage();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<String>(
      future: _messageFuture,
      builder: (context, snapshot) {
        // ConnectionState.waiting === Promise pending
        if (snapshot.connectionState == ConnectionState.waiting) {
          return const Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              CircularProgressIndicator(),
              SizedBox(height: 16),
              Text(
                'Awaiting the future...',
                style: TextStyle(color: Colors.grey, fontSize: 14),
              ),
            ],
          );
        }

        // snapshot.hasError === Promise rejected
        if (snapshot.hasError) {
          return Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              const Icon(Icons.error_outline, color: Colors.red, size: 40),
              const SizedBox(height: 12),
              Text(
                'Error: ${snapshot.error}',
                style: const TextStyle(color: Colors.red, fontSize: 14),
              ),
            ],
          );
        }

        // snapshot.data present === Promise resolved
        return Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Icon(Icons.check_circle_outline,
                color: Colors.green, size: 48),
            const SizedBox(height: 12),
            Text(
              snapshot.data ?? '',
              style: const TextStyle(
                  fontSize: 22, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 8),
            const Text(
              'Future resolved — like a Promise.then() callback.',
              style: TextStyle(color: Colors.grey, fontSize: 13),
              textAlign: TextAlign.center,
            ),
          ],
        );
      },
    );
  }
}
What is the Dart equivalent of JavaScript's `.catch()` in a Promise chain?
Which Dart API runs multiple Futures concurrently and resolves when all complete?
When wrapping a callback-based API to return a Future manually, which Dart class do you use?