2.0 Асинхронность. Future
Future - Это асинхронная операция, которая при запуске не может сразу выдать результат. Она пригодиться для:
Выборки данных по сети
Запись в базу данных
Чтение данных из файла и др.
Future возвращает любой тип Future<T>. Тип void используется когда функция Future не возвращает какой либо полезный результат.
Из официальной документации:
Если future не выдает полезного значения, то тип future равен Future<void>.
Может находиться в одном из двух состояний: незавершенное или завершенное.
Когда вы вызываете функцию, которая возвращает будущее, функция ставит в очередь выполняемую работу и возвращает незавершенное будущее.
Когда операция future завершается, future завершается значением или ошибкой.
Вспомним о последовательности выполнения
void main() {
final myFuture = Future(() {
print('Завершена фьюча');
return 400;
});
print('main() закончился.');
}
//Console
//main() закончился.
//Завершена фьюча
Как ты видишь, завершается весь main до того, как функция Future выполнится.
Возможности работы с Future
THEN и DELAYED
При определении объекта Future он находится в незавершенном состоянии и будет выполнен чуть позже. Но как же поймать тот момент, когда Future уже выполнен и перешел в завершенное состояние? Для этого у Future определен метод then.
Этот метод принимает функцию обратного вызова, которая будет срабатывать при завершении Future. В качестве единственного параметра функция принимает полученное из Future значение.
Сначала с помощью конструктора Future.delayed создаем future с задержкой 5 сек и возращает затем значение Future<String> :
Future<String> future = Future.delayed(
Duration(seconds: 5),
() {
print("Выполнение Future");
return "возвращаемое значение";
});
А затем добавим then, так мы видим что возвращенное знание из фьючи передалось в then:
future.then((value){
print("Получено значение: $value");
});
Если же мы возвращаем void, а не значение вместо value в then ставим прочерк(_).
При необходимости можно делать цепочки из then.
Так же можно передать значение из одного then в другой.
Отслеживание ошибок: как поймать кота?
then() и catchError() являются распространенным шаблоном при работе Future, и их можно рассматривать как грубый эквивалент блоков try-catch.
Независимо от того, возникла ошибка внутри myFunc() или внутри then(), catchError() успешно обрабатывает ее.
А вот пример работы catchError() и onError вместе из официальной документации:
Цитата рекомендации:
В общем случае не рекомендуется реализовывать две разные стратегии обработки ошибок: регистрируйте второй обратный вызов только в том случае, если есть веская причина для обнаружения ошибки внутри then().
В таких случаях самый простой вариант это Future.sync():
Future.sync() делает ваш код устойчивым к перехваченным исключениям. Если в вашей функции смесь синхронного и асинхронного кода вероятно там будет ошибка.
Future.sync() не только позволяет обрабатывать ошибки, но также предотвращает случайную утечку ошибок из вашей функции.
Примеры: https://dart.dev/guides/libraries/futures-error-handling
Есть споры о том что эквивалентен ли Future.value и Future.sync. На первый взгляд некоторым казалось что да но на самом деле это нет так. Прочтите это:
а так же это https://github.com/dart-lang/sdk/issues/44751
Future.error
Создан для того чтобы из фьючи возвращать ошибки.
Future.error
return Future.error('Error');
Future.microtask
Для выполнения особо важных асинхронных операций.
void main() {
print('future');
Future(() => print('future 1'));
Future(() => print('future 2'));
print('microtask');
// Microtasks will be executed before futures.
Future.microtask(() => print('microtask 1'));
Future.microtask(() => print('microtask 2'));
Этот пример в DartPad https://dartpad.dev/49bde0c1ed780decc902f3d4d06d8f0c.
Подробнее о microtask мы писали в прошлой статье.
Интересное и полезное про последовательность: https://pmatatias.medium.com/flutter-future-future-sync-future-microtask-future-value-etc-3f46aeae1210
FutureOr class
Тип FutureOr<int> на самом деле представляет собой «объединение типов» типов int и Future<int>.
Для чего нам он нужен? Например когда мы следуем интерфейсу и реализуем метод Future<String> fetch();
Мы обязаны реализовывать асинхронную функцию, а нам не всегда это нужно.
FutureOr<String> fetch();
позволяет возвращать тип Future<String> и String.
Таким образом мы сможем реализовывать и синхронную функцию и асинхронную.