Библиотека dart:async

Библиотека dart:async предоставляет основные возможности асинхронного программирования на Dart: Future и Stream.

  1. Для работы с Future / Stream не всегда требуется ручной импорт dart:async, так как он уже экспортируется dart:core;
  2. Если вы используете типы StreamController, StreamSubscription, ParallelWaitError и другие, нужно явно подключить библиотеку:
import 'dart:async';Code language: JavaScript (javascript)

Два ключевых понятия:

  • Future: Представляет одноразовый асинхронный результат (в будущем вернёт ровно одно значение либо одну ошибку)
  • Stream: Представляет непрерывную последовательность асинхронных данных (генерирует множество значений, похоже на поток событий)

Future

1. async / await

Предпочтительный вариант, читаемость выше цепочки вызовов .then()

  • Функции с модификатором async возвращают Future;
  • await ожидает завершения Future, приостанавливая выполнение кода до получения результата;
  • Используйте try-catch для перехвата асинхронных исключений.
// Асинхронные функции возвращают Future
Future<String> findEntryPoint() async => "main.dart";
Future<int> runExecutable(String entry, List args) async => 0;
Future<void> flushThenExit(int code) async {}

// Синтаксис с await (рекомендуется)
Future<void> runUsingAsyncAwait() async {
  try {
    var entryPoint = await findEntryPoint();
    var exitCode = await runExecutable(entryPoint, []);
    await flushThenExit(exitCode);
  } catch (e) {
    // Общий перехват ошибок
    print("Произошла ошибка: $e");
  }
}

2. Традиционная цепочка .then() / .catchError()

  • .then(): Запускает обратный вызов после успешного завершения Future; возвращает новый Future для продолжения цепочки
  • .catchError(): Перехватывает исключения; обязательно вызывайте после результата then, иначе ошибки внутри then не будут обработаны
void runUsingFuture() {
  findEntryPoint()
      .then((entryPoint) {
        return runExecutable(entryPoint, []);
      })
      .then(flushThenExit)
      .catchError((e) {
        print("Исключение: $e");
      });
}

3. Ожидание нескольких параллельных Future

① Future.wait()

Ожидает завершения всех задач; при сбое любого Future вся операция сразу завершается с ошибкой

Future<void> task1() async {}
Future<void> task2() async {}
Future<void> task3() async {}

void testWait() async {
  await Future.wait([
    task1(),
    task2(),
    task3(),
  ]);
  print("Все задачи выполнены");
}
② List.wait / Record.wait (новые возможности, гибкая обработка параллельных ошибок)

Генерирует исключение ParallelWaitError, позволяет определить, какие задачи завершились успешно, а какие завершились с ошибкой, они не прерывают друг друга.

Future<int> delete() async => 1;
Future<String> copy() async => "ok";
Future<bool> errorTask() async {
  throw Exception("Ошибка задачи");
}

void main() async {
  try {
    // Вариант со списком
    var results = await [delete(), copy(), errorTask()].wait;
  } on ParallelWaitError<List<Object?>, List<AsyncError?>> catch (e) {
    print(e.values);   // Значение при успехе, null при ошибке
    print(e.errors);   // null при успехе, хранит информацию об ошибке при сбое
  }

  // Вариант с кортежем Record: поддерживает Future разных типов
  try {
    final (resInt, resStr, resBool) = await (delete(), copy(), errorTask()).wait;
  } on ParallelWaitError<(int?, String?, bool?), (AsyncError?, AsyncError?, AsyncError?)> catch(e) {

  }
}

Stream

Stream — это непрерывный поток асинхронных данных (построчное чтение файлов, события нажатия кнопок, сетевые потоки).

Два способа использования

1: await for асинхронный цикл (краткий синтаксис, похож на синхронный обход)

Подходит для конечных потоков данных; не используйте для бесконечных потоков событий (например, клики по интерфейсу), это заблокирует остальной код.

import 'dart:io';

Future<void> readFileByAwaitFor() async {
  var file = File("config.txt");
  Stream<List<int>> input = file.openRead();
  var lines = input
      .transform(utf8.decoder)
      .transform(const LineSplitter());

  try {
    await for (final line in lines) {
      print("Прочитана строка: $line");
    }
    print("Чтение файла завершено");
  } catch (e) {
    print("Ошибка чтения $e");
  }
}
2 .listen() подписка на поток (для бесконечных потоков)
inputStream.listen(
  (data) {
    // Обратный вызов при получении данных
  },
  onError: (err) {
    // Произошла ошибка
  },
  onDone: () {
    // Поток нормально закрыт
  },
);

Метод listen возвращает объект StreamSubscription, через него можно вызвать .cancel() для отмены подписки.

Распространённые методы работы с Stream

  • Фильтрация: where()
  • Ограничение количества элементов: take(), takeWhile()
  • Пропуск элементов: skip(), skipWhile()
  • Получение одного элемента: first, last, single, firstWhere()

Преобразование данных Stream через transform()

Изменяет тип данных потока с помощью трансформеров (типичный случай: преобразование бинарного потока в текст)

var lines = inputStream
    .transform(utf8.decoder)       // List<int> → String
    .transform(const LineSplitter()); // String → отдельные строки

Как выбрать: listen() или await for

  1. Чтение файлов, конечные последовательности данных → await for
  2. События интерфейса, постоянные сообщения и другие бесконечные потоки.listen()
  3. После запуска await for код ждёт закрытия потока; при бесконечном потоке произойдёт блокировка

Библиотека dart:async

Previous:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *