Dart executes code on a single thread by default. Asynchronous programming with Future and Stream prevents blocking on I/O. For CPU-heavy work, use Isolate.
Future
A Future<T> represents a value of type T available at some point in the future.
// Creating
Future<String> fetchName() async {
await Future.delayed(Duration(seconds: 1));
return 'Ada';
}
// Immediately completed
Future.value(42);
Future.error(Exception('failed'));
// Delayed
Future.delayed(Duration(seconds: 2), () => 'done');
// Using async/await
void main() async {
var name = await fetchName();
print(name); // Ada
}Error handling
try {
var result = await fetchData();
} on FormatException catch (e) {
print('Format error: $e');
} catch (e) {
print('Error: $e');
} finally {
cleanup();
}
// then/catchError (alternative)
fetchData()
.then((data) => process(data))
.catchError((e) => handleError(e))
.whenComplete(() => cleanup());Combining futures
// Wait for all
var results = await Future.wait([
fetchUser(),
fetchPosts(),
fetchComments(),
]);
// Wait for any (first to complete)
var fastest = await Future.any([
server1.fetch(),
server2.fetch(),
]);
// Timeout
var result = await fetchName().timeout(Duration(seconds: 5));Stream
A Stream<T> is a sequence of asynchronous events.
// Creating with async*
Stream<int> countDown(int from) async* {
for (var i = from; i >= 0; i--) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
// Creating with StreamController
import 'dart:async';
final controller = StreamController<String>();
controller.add('hello');
controller.add('world');
controller.close();
controller.stream.listen(
(event) => print(event),
onError: (error) => print('Error: $error'),
onDone: () => print('Done'),
);Stream transformations
stream
.map((e) => e.toUpperCase())
.where((e) => e.startsWith('H'))
.distinct()
.take(5)
.skip(2)
.expand((e) => [e, e])
.asyncMap((e) async {
await Future.delayed(Duration(milliseconds: 100));
return e;
})
.asyncExpand((e) => Stream.fromIterable([e, e]));
// Convert to list
var all = await stream.toList();
// First and last
var first = await stream.first;
var last = await stream.last;
// Fold
var total = await stream.fold<int>(0, (acc, e) => acc + e);Broadcast streams
// Single-subscription (default) — one listener at a time
var stream = Stream.fromIterable([1, 2, 3]);
// Broadcast — multiple listeners
var broadcast = stream.asBroadcastStream();
broadcast.listen((e) => print('A: $e'));
broadcast.listen((e) => print('B: $e'));
// Broadcast controller
var controller = StreamController<String>.broadcast();Completer
Manually complete a Future:
import 'dart:async';
Future<T> withTimeout<T>(Future<T> future, Duration timeout) {
var completer = Completer<T>();
future.then(completer.complete).catchError(completer.completeError);
Future.delayed(timeout, () {
if (!completer.isCompleted) {
completer.completeError(TimeoutException('Timeout'));
}
});
return completer.future;
}Isolates
For CPU-intensive work, use isolates to run code in parallel:
import 'dart:isolate';
// Simple isolate (Dart 2.19+)
Future<int> heavyComputation(int count) async {
return await Isolate.run(() {
var sum = 0;
for (var i = 0; i < count; i++) {
sum += i;
}
return sum;
});
}
// Using
void main() async {
var result = await heavyComputation(1000000);
print(result);
}Isolate with message passing
import 'dart:isolate';
void worker(SendPort sendPort) {
var result = fibonacci(40);
sendPort.send(result);
}
int fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
Future<void> main() async {
var receivePort = ReceivePort();
await Isolate.spawn(worker, receivePort.sendPort);
var result = await receivePort.first;
print(result);
}Compute in Flutter
import 'package:flutter/foundation.dart';
// Flutter's compute function
var result = await compute(heavyWork, data);
int heavyWork(int input) {
// runs in a separate isolate
return input * 2;
}