Библиотека dart:async предоставляет основные возможности асинхронного программирования на Dart: Future и Stream.
- Для работы с
Future / Streamне всегда требуется ручной импортdart:async, так как он уже экспортируется dart:core;- Если вы используете типы
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
- Чтение файлов, конечные последовательности данных →
await for - События интерфейса, постоянные сообщения и другие бесконечные потоки →
.listen() - После запуска await for код ждёт закрытия потока; при бесконечном потоке произойдёт блокировка
Библиотека dart:async