async/await in Depth

import 'dart:async';

Future<User> loadUser(String id) async {
  final response = await http.get(Uri.parse('https://api.example.com/users/$id'));
  if (response.statusCode != 200) {
    throw HttpException('Failed: ${response.statusCode}');
  }
  return User.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
}

Future<void> main() async {
  try {
    final user = await loadUser('42');
    print(user.name);
  } on HttpException catch (e) {
    print('HTTP error: $e');
  } catch (e) {
    print('Other error: $e');
  }
}

25.1 Sequential vs Parallel

// Sequential (slow): each waits for the previous one
final a = await fetchA();
final b = await fetchB();

// Parallel (fast): start both, then await
final futureA = fetchA();
final futureB = fetchB();
final (resA, resB) = await (futureA, futureB).wait;

25.2 Async Pitfalls

Unawaited futures: errors can be lost. The unawaited_futures / discarded_futures lints catch this. Use unawaited(f) from dart:async to signal intent.

async in forEach: list.forEach((x) async => ...) doesn't wait. Use for loops or Future.wait.

Uncaught async errors: handle them with try/catch around await, .catchError, or zone handlers (Module 28).

Blocking the event loop: heavy synchronous computation freezes UI and I/O. Move it to an isolate (Module 27).

FutureOr<T> lets an API accept either a value or a Future.