Dart has single-threaded execution by default. Async operations use Future and Stream with async/await syntax.
Future
A Future represents a value that will be available later.
// Creating a future
Future<String> fetchName() async {
await Future.delayed(Duration(seconds: 1));
return 'Ada';
}
// Using async/await
void main() async {
var name = await fetchName();
print(name); // Ada
}
Future constructors and methods
// Completed futures
Future.value(42); // immediately completes with 42
Future.error('something went wrong'); // immediately completes with error
// Delayed
Future.delayed(Duration(seconds: 2), () => 'done');
// Wait for multiple futures
var results = await Future.wait([
fetchName(),
fetchAge(),
]);
// Wait for any
var fastest = await Future.any([
server1.fetch(),
server2.fetch(),
]);
// Timeout
var result = await fetchName().timeout(Duration(seconds: 5));
// Handle errors
try {
var result = await fetchName();
} catch (e) {
print('Error: $e');
}
// then/catchError (alternative to async/await)
fetchName()
.then((name) => print(name))
.catchError((error) => print('Error: $error'));
whenComplete
try {
var result = await fetchData();
} catch (e) {
print('Error: $e');
} finally {
cleanup(); // always runs
}
// Alternative with whenComplete on Future
fetchData()
.then((data) => process(data))
.catchError((e) => handleError(e))
.whenComplete(() => cleanup());
Stream
A Stream is a sequence of asynchronous events.
// Creating a stream from a function
Stream<int> countDown(int from) async* {
for (var i = from; i >= 0; i--) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
// Listening to a stream
countDown(3).listen((value) {
print(value); // 3, 2, 1, 0
});
// Using await for
await for (var value in countDown(3)) {
print(value);
}
Stream controllers
import 'dart:async';
final controller = StreamController<String>();
// Emit events
controller.add('hello');
controller.add('world');
controller.close(); // done
// Listen
controller.stream.listen((event) {
print(event);
});
Stream transformations
stream
.map((e) => e.toUpperCase())
.where((e) => e.startsWith('H'))
.distinct()
.take(5)
.skip(2);
// Debounce
stream.asyncMap((event) async {
await Future.delayed(Duration(milliseconds: 300));
return event;
});
// Convert to list
var all = await stream.toList();
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 (Dart’s model for concurrency):
import 'dart:isolate';
Future<int> heavyComputation(int count) async {
return await Isolate.run(() {
var sum = 0;
for (var i = 0; i < count; i++) {
sum += i;
}
return sum;
});
}
Compute with message passing
Future<void> main() async {
var result = await Isolate.run(() {
// This runs in a separate isolate
return fibonacci(40);
});
print(result);
}
int fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
Next: Back to Dart overview