Async

Dart asynchronous programming with Futures, Streams, and Isolates.

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