Что такое будущее и как его использовать?

Я получаю следующую ошибку:

A value of type 'Future<int>' can't be assigned to a variable of type 'int'

Это может быть другой тип вместо int, но в основном шаблон

A value of type 'Future<T>' can't be assigned to a variable of type 'T'

Так...

  • Что именно такое Future?
  • Как мне получить реальную ценность, которую я хочу получить?
  • Какой виджет использовать для отображения моей стоимости, когда все, что у меня есть, Future<T>?

6 ответов

Если вы знакомы с Task<T> или Promise<T> и async/ await шаблон, то вы можете сразу перейти к разделу "Как использовать будущее с виджетами во Flutter".

Что такое будущее и как его использовать?

Что ж, в документации сказано:

Объект, представляющий отложенное вычисление.

Это правильно. Также он немного абстрактен и суховат. Обычно функция возвращает результат. Последовательно. Функция вызывается, запускается и возвращает результат. А пока звонящий ждет. Некоторым функциям, особенно когда они обращаются к таким ресурсам, как оборудование или сеть, требуется немного времени. Представьте, что изображение аватара загружается с веб-сервера, данные пользователя загружаются из базы данных или просто тексты приложения на нескольких языках загружаются из памяти устройства. Это может быть медленным.

Большинство приложений по умолчанию имеют единый поток управления. Когда этот поток блокируется, например, из-за ожидания вычислений или доступа к ресурсам, которые требуют времени, приложение просто зависает. Вы можете помнить это как стандарт, если вы достаточно взрослые, но в современном мире это будет считаться ошибкой. Даже если что-то требует времени, мы получаем небольшую анимацию. Спиннер, песочные часы, может быть, индикатор выполнения. Но как приложение может запускаться и показывать анимацию, но при этом ждать результата? Ответ: асинхронные операции. Операции, которые все еще выполняются, пока ваш код чего-то ждет. Теперь, как компилятор узнает, действительно ли он должен останавливать все и ждать результата или продолжать всю фоновую работу и ждать только в этом случае? Что ж, он не может понять это самостоятельно. Мы должны сказать Это.

Это достигается с помощью шаблона, известного как async и await. Это не специфично для флаттера или дротика, оно существует под тем же именем на многих других языках. Вы можете найти документацию по Dart здесь.

Поскольку метод, который требует некоторого времени, не может вернуться немедленно, он вернет обещание доставки значения, когда это будет сделано.

Это называется Future. Таким образом, обещание загрузить число из базы данных вернетFuture<int> а обещание вернуть список фильмов из поиска в Интернете может вернуть Future<List<Movie>>. АFuture<T>это то, что в будущем даст вамT.

Давайте попробуем другое объяснение:

Будущее представляет собой результат асинхронной операции и может иметь два состояния: незавершенное или завершенное.

Скорее всего, поскольку вы делаете это не просто для развлечения, вам действительно нужны результаты этого Future<T>для прогресса в вашем приложении. Вам нужно вывести номер из базы или список найденных фильмов. Итак, вы хотите подождать, пока не будет результата. Это гдеawait приходит в:

Future<List<Movie>> result = loadMoviesFromSearch(input);

// right here, you need the result. So you wait for it:
List<Movie> movies = await result;

Но подождите, разве мы не прошли полный круг? Разве мы снова не ждем результата? Да, действительно. Программы были бы в высшей степени хаотичными, если бы они не имели некоторого сходства с последовательным потоком. Но дело в том, что с помощью ключевого словаawaitмы сказали компилятору, что на данном этапе, пока мы хотим дождаться результата, мы не хотим, чтобы наше приложение просто зависало. Мы хотим, чтобы все остальные выполняемые операции, например, анимация, продолжались.

Однако вы можете использовать только awaitключевое слово в функциях, которые сами помечены как async и вернуть Future<T>. Потому что когда тыawaitчто-то, то ожидающая функция больше не может немедленно вернуть свой результат. Вы можете вернуть только то, что у вас есть, если вам нужно дождаться этого, вы должны вернуть обещание, чтобы доставить его позже.

Future<Pizza> getPizza() async {
    Future<PizzaBox> delivery = orderPizza();        

    var pizzaBox = await delivery;

    var pizza = pizzaBox.unwrap();
    
    return pizza;   
}

Наша функция getPizza должна ждать пиццы, поэтому вместо возвратаPizzaнемедленно, он должен вернуть обещание, что пицца будет там в будущем. Теперь вы можете, в свою очередь,await функция getPizza где-нибудь.

Как использовать Future с виджетами во Flutter?

Все виджеты во флаттере ожидают реальных значений. Не обещание ценности, которая появится позже. Когда кнопке нужен текст, она не может использовать обещание, что текст появится позже. Необходимо, чтобы отобразить кнопку Теперь, поэтому она нуждается в тексте прямо сейчас.

Но иногда все, что у вас есть, это Future<T>. Вот где FutureBuilder входит. Вы можете использовать его, когда у вас есть будущее, чтобы отобразить одно, пока вы его ждете (например, индикатор прогресса), и другое, когда оно будет выполнено (например, результат).

Давайте посмотрим на наш пример пиццы. Вы хотите заказать пиццу, вам нужен индикатор прогресса, пока вы его ждете, вы хотите видеть результат, когда он будет доставлен, и, возможно, вывести сообщение об ошибке при возникновении ошибки:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

/// ordering a pizza takes 5 seconds and then gives you a pizza salami with extra cheese
Future<String> orderPizza() {
  return Future<String>.delayed(Duration(seconds: 5), () async => 'Pizza Salami, Extra Cheese');
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark(),
      home: Scaffold(
        body: Center(
          child: PizzaOrder(),
        ),
      ),
    );
  }
}

class PizzaOrder extends StatefulWidget {
  @override
  _PizzaOrderState createState() => _PizzaOrderState();
}

class _PizzaOrderState extends State<PizzaOrder> {
  Future<String> delivery;

  @override
  Widget build(BuildContext context) {
    return Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          RaisedButton(
            onPressed: delivery != null ? null : () => setState(() { delivery = orderPizza(); }),
            child: Text('Order Pizza Now')
          ),
          delivery == null
            ? Text('No delivery scheduled')
            : FutureBuilder(
              future: delivery,
              builder: (context, snapshot) {
                if(snapshot.hasData) {
                  return Text('Delivery done: ${snapshot.data}');
                } else if(snapshot.hasError) {
                  return Text('Delivery error: ${snapshot.error.toString()}');
                } else {
                  return CircularProgressIndicator();
                }
              })
        ]);
  }
}

Вот как вы используете FutureBuilder для отображения результата своего будущего, когда оно у вас есть.

Вот список аналогий Дарта из других языков:

  • JS: Promise
  • Ява:
  • Пихтон: Future
  • C #: Task

Как и в других языках, Future - это особый тип объекта, который позволяет использовать синтаксический сахар async / await, писать асинхронный код синхронно / линейно. Вы возвращаете Future из асинхронного метода, а не принимаете обратный вызов в качестве параметра и избегаете ада обратных вызовов - и Futures, и обратные вызовы решают одни и те же проблемы (запускают некоторый код позже), но по-разному.

Future<T> возвращая потенциальное значение, которое будет выполнено async Работа

Например:

Future<int> getValue() async {
    return Future.value(5);
  }

Выше код возвращается Future.value(5) который из int type, но при получении значения из метода мы не можем использовать type Future<int> т.е.

Future<int> value = await getValue();  // Not Allowed
// Error
A value of type 'Future<int>' can't be assigned to a variable of type 'int'

Чтобы решить вышесказанное, getValue() должен быть получен под int тип

  int value = await getValue(); // right way as it returning the potential value. 

Надеюсь, этот ключевой момент будет информативным, я покажу его двумя разными методами Async:

Обратите внимание на следующий метод, где и - внутренние методы.

Если я поставлю ключевое слово раньше, операция ждет, пока она не будет завершена, а затем перейдет к следующей строке, но я намеренно удалил, потому что мне нужно, чтобы мой диалог загрузки отображался одновременно с обработкой, поэтому это означает showLoading() а также getAllCarsFromApi()методы обрабатываются в разных потоках . Наконец-то hideLoading() скрывает диалог загрузки.

      Future<List<Car>> getData() async{
   showLoading();
   final List<Car> cars = await getAllCarsFromApi();
   hideLoading();
   return cars;
}

Теперь посмотри на это другое Async метод, здесь getCarByIdFromApi() требуется метод, который рассчитывается из getCarIdFromDatabase(), поэтому должен быть awaitключевое слово перед первым методом, заставляющим операцию ждать, пока idвычисляется и передается второму методу. Итак, здесь два метода обрабатываются один за другим и в одном потоке .

      Future<Car> getCar() async{
   int id = await getCarIdFromDatabase();
   final Car car = await getCarByIdFromApi(id);
   return car;
}

Простой ответ заключается в том, что если функция возвращает свое значение с delayнекоторое время, Futureиспользуется для получения его значения.

      Future<int> calculate({required int val1, required int val2}) async {
    await Future.delayed(const Duration(seconds: 2));
    return val1 + val2;
  }

если мы вызовем вышеуказанную функцию как

      getTotal() async { 

      int result = calculate(val1: 5, val2: 5);

    print(result);
  }

мы получим следующую ошибку:

      A value of type 'Future<int>' can't be assigned to a variable of type 'int'

но если мы используем await перед вызовом функции, он даст фактическое возвращаемое значение из функции после задержки

      getTotal() async { 

    int result = await calculate(val1: 5, val2: 5);

   print(result);
 }

ключевое слово asyncнеобходимо использовать awaitдля будущего, чтобы получить возвращаемое значение

Я пытаюсь привести очень простой пример. Предположим, вы заказали что-то онлайн, пусть это будет рубашка. затем вам нужно дождаться, пока заказ будет отправлен и доставлен к вам домой. В то же время вы не перестанете заниматься своими повседневными делами / работать, чем бы вы ни занимались, и через день, если он будет доставлен к вам домой, вы заберете его и наденете. Теперь посмотрите на следующий пример.

Хорошо, теперь давайте создадим функцию, которая обрабатывает доставку нашего заказа. ( Также читайте комментарии )

      //order function which will book our order and return our order(which is our shirt). don't focus on Order object type just focus on how this function work and you will get to know about future definitely.
Future<Order> orderSomething(){
    //here our order processing and it will return our order after 24 hrs :) 
    await Future.delayed(const Duration(hours: 24),() => Order('data'));
    
}

Сейчас

      void main() {
//now here you have called orderSomething() and you dont want to wait for it to be delivered
//you are not dependent on your order to do your other activities 
// so when your order arrives you will get to know
    orderSomething()
    wearSomething()
    goingCollege()
}

Теперь, если вы зависите от своего заказа, вам нужно добавить await async (я покажу вам, где)

       void main() async{
  //now you're dependent on your order you want to wait for your order
   await orderSomething()
   wearOrderedShirt()   // :)  
   goingCollege()
 }

Теперь в большинстве случаев в приложениях флаттера вам придется ждать ваших вызовов API (сети), фоновой задачи для загрузки/выгрузки, вызовов базы данных и т. д.

Другие вопросы по тегам