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
Консоль
Консоль

Так же можно передать значение из одного then в другой.

Добавлено return в then
Добавлено return в then

Отслеживание ошибок: как поймать кота?

then() и catchError() являются распространенным шаблоном при работе Future, и их можно рассматривать как грубый эквивалент блоков try-catch.

Независимо от того, возникла ошибка внутри myFunc() или внутри then(), catchError() успешно обрабатывает ее.

А вот пример работы catchError()  и onError вместе из официальной документации:

тут ошибка из future обрабатывается  onError, а ошибка из then catchError()
тут ошибка из future обрабатывается  onError, а ошибка из then catchError()

Цитата рекомендации:

В общем случае не рекомендуется реализовывать две разные стратегии обработки ошибок: регистрируйте второй обратный вызов только в том случае, если есть веская причина для обнаружения ошибки внутри then().

В таких случаях самый простой вариант это Future.sync():

Future.sync() делает ваш код устойчивым к перехваченным исключениям. Если в вашей функции смесь синхронного и асинхронного кода вероятно там будет ошибка.

Future.sync() не только позволяет обрабатывать ошибки, но также предотвращает случайную утечку ошибок из вашей функции.

Примеры: https://dart.dev/guides/libraries/futures-error-handling

Есть споры о том что эквивалентен ли Future.value и Future.sync. На первый взгляд некоторым казалось что да но на самом деле это нет так. Прочтите это:

https://pmatatias.medium.com/flutter-future-future-sync-future-microtask-future-value-etc-3f46aeae1210

а так же это 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.

Таким образом мы сможем реализовывать и синхронную функцию и асинхронную.

Комментарии (3)


  1. PackRuble
    13.08.2023 06:46
    +3

    Если же мы возвращаем void, а не значение вместо value в then ставим прочерк(_).

    Это не так. Я перечитал несколько раз данное предложение и это всё ещё не так. Мы ставим прочерк в том случае, когда аргумент нам не нужен. Мы не собираемся его использовать, вот и всё. Это лучше выражает наши намерения и делает код семантически ясней.


    1. programiss Автор
      13.08.2023 06:46

      Да, действительно можно так сказать, спасибо за комментарий )


  1. PackRuble
    13.08.2023 06:46

    Что же касается имеющейся чисто технической информации, то вам нужно поработать над подачей и оформлением. Минимально – перечитать ещё раз и обратить внимание на семантические и синтаксические ошибки, применить `dart format` к коду и выбрать язык dart для подсветки синтаксиса, проследить полноту информации ваших скриншотов. Сейчас читать это близко к невозможному.