DartJuniorCoding

Что такое Dart Futures и как использовать async/await?

Future<T> представляет результат асинхронной операции; async/await — синтаксический сахар, позволяющий писать асинхронный код линейно, не блокируя однопоточный event loop Dart.

Что такое Future в Dart

Future<T> — объект из dart:async, представляющий результат асинхронной операции, который будет доступен в будущем. Future находится в одном из трёх состояний: uncompleted, completed with value, completed with error. Dart использует однопоточный event loop с двумя очередями: event queue (I/O, таймеры) и microtask queue (Future.value, scheduleMicrotask). Microtask-очередь всегда опустошается перед следующим событием из event queue.

async/await — синтаксический сахар над Future

Функция с модификатором async автоматически возвращает Future<T>. Ключевое слово await приостанавливает выполнение функции до завершения Future, не блокируя event loop — в этот момент могут выполняться другие задачи из очередей.

import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;

// Простой пример: цепочка Future через async/await
Future<Map<String, dynamic>> fetchUser(int id) async {
  final uri = Uri.parse('https://jsonplaceholder.typicode.com/users/$id');
  final response = await http.get(uri);

  if (response.statusCode != 200) {
    throw Exception('HTTP ${response.statusCode}');
  }

  return jsonDecode(response.body) as Map<String, dynamic>;
}

// Параллельные Future через Future.wait
Future<void> loadAll() async {
  // Последовательно: ~2 секунды
  // final a = await fetchUser(1);
  // final b = await fetchUser(2);

  // Параллельно: ~1 секунда
  final results = await Future.wait([
    fetchUser(1),
    fetchUser(2),
  ]);

  print(results[0]['name']); // Leanne Graham
  print(results[1]['name']); // Ervin Howell
}

// Обработка ошибок
Future<String> safeLoad(int id) async {
  try {
    final user = await fetchUser(id);
    return user['name'] as String;
  } on Exception catch (e) {
    print('Error: $e');
    return 'Unknown';
  }
}

void main() async {
  print(await safeLoad(1)); // Leanne Graham
  await loadAll();
}

Полезные методы Future

  • Future.value(x) — Future, уже завершённый значением; callback вызывается в microtask очереди.
  • Future.error(e) — Future с ошибкой.
  • Future.delayed(duration, fn) — выполняет fn через заданное время.
  • Future.wait(list) — ждёт все Future параллельно, возвращает список результатов.
  • Future.any(list) — завершается, как только завершится первый Future.
  • .then(fn), .catchError(fn), .whenComplete(fn) — callback-стиль (альтернатива async/await).
  • .timeout(duration) — бросает TimeoutException если Future не завершился вовремя.

Event loop и порядок выполнения

void main() {
  print('1: sync');                              // сразу
  Future.value('microtask').then((_) => print('2: microtask')); // microtask queue
  Future(() => print('3: event queue'));         // event queue
  print('4: sync');                              // сразу
}
// Вывод: 1: sync, 4: sync, 2: microtask, 3: event queue

Подводные камни

  • Забытый await: вызов asyncFn() без await не ждёт завершения; функция запускается, но ошибки и результат теряются — используйте unawaited(asyncFn()) из dart:async для явного намерения.
  • Блокировка event loop: тяжёлые синхронные вычисления внутри async-функции блокируют весь event loop Flutter; выносите их в compute() или Isolate.run().
  • async в initState: Flutter запрещает await в initState; используйте паттерн Future.microtask(() async { ... }) или WidgetsBinding.instance.addPostFrameCallback.
  • BuildContext после await: контекст может стать невалидным после await; проверяйте mounted у StatefulWidget или используйте ref.read в Riverpod.
  • Необработанные ошибки в Future.wait: при ошибке одного Future весь Future.wait бросает исключение; остальные Future продолжают выполняться — для изоляции используйте eagerError: false или индивидуальные try-catch.
  • then() vs await: вложенные .then() быстро превращаются в callback hell; предпочитайте async/await для линейного кода.

Common mistakes

  • Сводить «Dart Futures и как использовать async/await» к синтаксису и не объяснять event loop.
  • Игнорировать жизненный цикл, основной поток или момент освобождения ресурсов в сценарии dart-6.
  • Выбирать API по привычке, не проверяя состояние, ошибки, доступность и платформенные ограничения.

What the interviewer is testing

  • Формулирует точную модель для «Dart Futures и как использовать async/await» и подтверждает ее корректным примером.
  • Умеет связать ответ с Future и Stream, тестированием и отладкой на устройстве.
  • Называет ограничения подхода dart-6, включая производительность, память и сопровождение.

Sources

Related topics

Что такое Dart Futures и как использовать `async`/`await`? | Talanto