¿Qué es un futuro y cómo lo uso?

Resuelto nvoigt asked hace 4 años • 5 respuestas

Obtuve el siguiente error:

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

Podría ser otro tipo en lugar de int, pero básicamente el patrón es:

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

Entonces:

  • ¿Qué es exactamente un Future?
  • ¿Cómo obtengo el valor real que quiero obtener?
  • ¿Qué widget uso para mostrar mi valor cuando todo lo que tengo es un Future<T>?
nvoigt avatar Jul 21 '20 21:07 nvoigt
Aceptado

En caso de que esté familiarizado con Task<T>o Promise<T>y el patrón async/ await, puede pasar directamente a la sección "Cómo usar un Future con los widgets en Flutter".

¿Qué es un futuro y cómo lo uso?

Bueno, la documentación dice:

Un objeto que representa un cálculo retrasado.

Eso es correcto. También es un poco abstracto y seco. Normalmente, una función devuelve un resultado. Secuencialmente. La función se llama, se ejecuta y devuelve su resultado. Hasta entonces, la persona que llama espera. Algunas funciones, especialmente cuando acceden a recursos como hardware o red, tardan un poco en hacerlo. Imagine que se carga una imagen de avatar desde un servidor web, que se cargan los datos de un usuario desde una base de datos o simplemente que se cargan los textos de la aplicación en varios idiomas desde la memoria del dispositivo. Puede que eso sea lento.

La mayoría de las aplicaciones tienen de forma predeterminada un único flujo de control. Cuando este flujo se bloquea, por ejemplo al esperar un cálculo o un acceso a recursos que lleva tiempo, la aplicación simplemente se congela. Es posible que recuerdes esto como estándar si tienes la edad suficiente, pero en el mundo actual eso se consideraría un error. Incluso si algo lleva tiempo, obtenemos una pequeña animación. Una ruleta, un reloj de arena, tal vez una barra de progreso. Pero, ¿cómo puede una aplicación ejecutarse y mostrar una animación y aún así esperar el resultado? La respuesta es: operaciones asincrónicas. Operaciones que aún se ejecutan mientras su código espera algo. Ahora bien, ¿cómo sabe el compilador si realmente debería detener todo y esperar un resultado o continuar con todo el trabajo en segundo plano y esperar solo en este caso? Bueno, no puede resolverlo por sí solo. Tenemos que contarlo .

Esto se logra mediante un patrón conocido comoasíncronoyesperar. No es específico dealeteoodardo, existe con el mismo nombre en muchos otros idiomas. Puede encontrar la documentación de Dart aquí .

Dado que un método que tarda algún tiempo no puede regresar inmediatamente, devolverá la promesa de entregar un valor cuando esté terminado.

Eso se llama Future. Entonces, la promesa de cargar un número de la base de datos devolvería un Future<int>tiempo. La promesa de devolver una lista de películas de una búsqueda en Internet podría devolver un archivo Future<List<Movie>>. A Future<T>es algo que en el futuro te dará un T.

Intentemos una explicación diferente:

Un futuro representa el resultado de una operación asincrónica y puede tener dos estados: incompleto o completado.

Lo más probable es que, como no estás haciendo esto sólo por diversión, en realidad necesites los resultados Future<T>para progresar en tu aplicación. Debe mostrar el número de la base de datos o la lista de películas encontradas. Entonces quieres esperar hasta que el resultado esté ahí. Aquí es donde awaitentra en juego:

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

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

Pero espera, ¿no hemos cerrado el círculo? ¿No estamos esperando de nuevo el resultado? Sí, efectivamente lo somos. Los programas serían completamente caóticos si no tuvieran cierta apariencia de flujo secuencial. Pero el punto es que usando la palabra clave awaitle hemos dicho al compilador que en este punto, mientras queremos esperar el resultado, no queremos que nuestra aplicación simplemente se congele. Queremos que continúen todas las demás operaciones en ejecución, como por ejemplo las animaciones.

Sin embargo, solo puede utilizar la awaitpalabra clave en funciones que estén marcadas como asyncy devuelvan un archivo Future<T>. Porque cuando haces awaitalgo, la función que está esperando ya no puede devolver su resultado inmediatamente. Sólo puedes devolver lo que tienes, si tienes que esperar, tienes que devolver una promesa de entregarlo más tarde.

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

    var pizzaBox = await delivery;

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

Nuestra función getPizza tiene que esperar la pizza, por lo que en lugar de regresar Pizzainmediatamente, tiene que devolver la promesa de que habrá una pizza allí en el futuro . Ahora puedes, a su vez, activar awaitla función getPizza en alguna parte.

¿Cómo usar un Future con los widgets en Flutter?

Todos los widgets en flutter esperan valores reales. No es una promesa de un valor que vendrá más adelante. Cuando un botón necesita un texto, no puede utilizar la promesa de que el texto aparecerá más tarde. Necesita mostrar el botón ahora , por lo que necesita el texto ahora .

Pero a veces lo único que tienes es un Future<T>. Ahí es donde FutureBuilderentra en juego. Puedes usarlo cuando tengas un futuro, para mostrar una cosa mientras lo esperas (por ejemplo, un indicador de progreso) y otra cosa cuando esté terminado (por ejemplo, el resultado).

Echemos un vistazo a nuestro ejemplo de pizza. Quiere pedir pizza, quiere un indicador de progreso mientras la espera, quiere ver el resultado una vez que se entrega y tal vez mostrar un mensaje de error cuando haya un error:

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(
         const 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: [
          ElevatedButton(
            onPressed: delivery != null 
                       ? null 
                       : () => setState(() { 
                            delivery = orderPizza(); 
                         }),
            child: const Text('Order Pizza Now')
          ),
          delivery == null
            ? const 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 const CircularProgressIndicator();
                }
              })
        ]);
  }
}

Así es como usas a FutureBuilderpara mostrar el resultado de tu futuro una vez que lo tienes.

nvoigt avatar Jul 21 '2020 14:07 nvoigt

Aquí hay una lista de analogías con Dart Futureen otros idiomas:

  • JS:Promise
  • Java:Future
  • Pitón:Future
  • C#:Task

Al igual que en otros lenguajes, Future es un tipo especial de objeto que permite utilizar la sintaxis async/await sugar, escribir código asincrónico de forma síncrona/lineal. Devuelve Future desde un método asíncrono en lugar de aceptar una devolución de llamada como parámetro y evitar el infierno de las devoluciones de llamada: tanto Futures como las devoluciones de llamada resuelven los mismos problemas (activando algo de código en un momento posterior) pero de una manera diferente.

Maxim Saplin avatar May 07 '2021 14:05 Maxim Saplin

Future<T>devolver el valor potencial que se obtendrá mediante asyncel trabajo

P.ej:

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

El código anterior devuelve Future.value(5)cuál es de inttipo, pero mientras recibimos el valor del método no podemos usar el tipo, es Future<int>decir

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

Para resolver lo anterior, getValue() debe recibirse en intel tipo

  int value = await getValue(); // right way as it returning the potential value. 
Jitesh Mohite avatar Aug 20 '2020 09:08 Jitesh Mohite