Diferencia entre revisiones de «Flutter DART Programación asíncrona»

De MediaWiki
Ir a la navegación Ir a la búsqueda
Línea 286: Línea 286:
  
 
* Ahora supón que los dos métodos acceden a Internet y son ejecutados en un hilo separado. Haz lo necesario para que todo siga funcionando igual.
 
* Ahora supón que los dos métodos acceden a Internet y son ejecutados en un hilo separado. Haz lo necesario para que todo siga funcionando igual.
−
: Haz que el método obtenerEdad tarde 5 segundos en ejecutarse y que el método obtenerPremio tarde 2 segundos.
+
: Haz que el método obtenerEdad tarde 4 segundos en ejecutarse y que el método obtenerPremio tarde 2 segundos.
 
:<syntaxhighlight lang="java" enclose="div" highlight="" >
 
:<syntaxhighlight lang="java" enclose="div" highlight="" >
 +
Future<int> obtenerEdad(String nombre){
 +
 
 +
  // ACCEDEMOS A INTERNET A BUSCAR LA EDAD
 +
  final alumnos = {'pedro' : 27, 'ana' : 35, 'luisa' : 22};
 +
 
 +
  if (!alumnos.containsKey(nombre)) return Future.value(0);
 +
 
 +
  return Future.delayed(const Duration(seconds: 4),() { print ('Obtemmos la edad...');
 +
                                                        return alumnos[nombre]!;});
 +
 +
}
 +
 +
Future<String> obtenerPremio(int edad){
 +
 
 +
  // ACCEDEMOS A INTERNET A BUSCAR LOS PREMIOS
 +
  final premios = ['Jamón', 'Coche','Piso'];
 +
 
 +
  return Future.delayed(const Duration(seconds: 2),(){ print('Obtemos el premio...');
 +
                                                      return premios[edad%3]; });
 +
 +
}
 +
 +
void main() async {
 +
 
 +
  int edad = await obtenerEdad('pedro');
 +
  String premio = await obtenerPremio(edad);
 +
 
 +
  print('Pedro con la edad de $edad, tiene de premio:$premio');
 +
 
 +
}
 
</syntaxhighlight>  
 
</syntaxhighlight>  
  

Revisión del 15:46 12 jul 2021

Introducción

  • La programación asíncrona hace referencia al uso de threads para ejecutar en modo 'paralelo' al hilo principal otra serie de tareas.



Futures

  • Más información:




void main() {

  print('Inicia programa');
  
  getImagenes('imagenes/animales').then(
    (datos) => print(datos)
  );
  
  print('Fin programa');
  
}

Future<List<String>> getImagenes(String path){
  
  List<String> datos = ['1.jpg','2.jpg','3.jpg','4.jpg'];
  
  
  return Future.delayed(Duration(seconds: 4),() => datos);  // Paramos el hilo durante 4 segundos
  
  
  // return Future.value(datos);
  
}



Gestión de errores

void main() {

  print('Inicia programa');
  
  try {
    getImages('imagenes/animales').then((datos) {
      datos.forEach((elem) => print('Dato:$elem'));
     }).catchError((e) { print(e);} );
    
  }
  catch(e){
    print('Excep:$e');
  }
  print('Fin programa');
  
}

Future<List<String>> getImages(String path){
  
  List<String> imagenes = ['1.jpg','2.jpg','3.jpg'];
  
  // Búsqueda de imágenes a partir del path
  
  //throw('Exepción no controlada');
  
  return Future.delayed(Duration(seconds: 3),() => Future.error('Error controlado'));  
}



Estado Completo

  • Un Future puede tener dos estados:
  • Incompleto: Cuando se está ejecutando.
  • Completo: Cuando ha terminado de ejecutarse. Este estado lo va a alcanzar haya o no error durante la ejecución del Future.


  • Es decir, el método then se va a ejecutar sólo cuando el Future acabe de ejecutarse correctamente mientras que el método whenComplete se ejecuta tanto si ha ido bien como si ha habido un error.
Veamos un ejemplo:
void main() {

  print('Inicia programa');
  
  cargarImagenes().then((datos) => print('Acabó el Future1 sin errores')).
      whenComplete(() => print('Terminó Future1'));

  cargarImagenesConError().
    then((datos) => print('Acabó el Future2 sin errores')).
    catchError((e) { print('Capturando el error:$e'); }).
    whenComplete(() => print('Terminó Future2'));

  print('Fin programa');
  
}
Future<void> cargarImagenesConError(){
  
 return Future.delayed(Duration(seconds: 2),() => throw('Error cargando imágenes')); 
  
}

Future<void> cargarImagenes(){
  
 return Future.delayed(Duration(seconds: 2),() { print('Cargamos imágenes');}); 
  
}
  • El Future cargarImagenes no devuelva nada (void) y se ejecuta sin errores, por lo tanto, la llamada a la función desde el hilo principal ejecuta el método then y el método whenComplete.
  • El Future cargarImagenesConError no devuelva nada (void) y se ejecuta con errores, por lo tanto, la llamada a la función desde el hilo principal ejecuta el método catchError y el método whenComplete.



Async/Await

  • Más información:
  • En los ejemplos anteriores vimos que para llamar a un Future y obtener el resultado disponemos del método then.
Pero existe otra forma de obtener el resultado de la llamada a un Future que es:
void main() {

  print('Inicia programa'); 
  
  final datos = getImages('imagenes/animales');
  print('Los datos son:$datos');
      
  print('Fin programa');
  
}

Future<List<String>> getImages(String path)  {
  
  List<String> imagenes = ['1.jpg','2.jpg','3.jpg'];
  
  // Búsqueda de imágenes a partir del path
  
  
  return Future.delayed(Duration(seconds: 3),() => imagenes);
  
}
El problema que tiene esta forma de obtener el resultado de un Future, es que, al ejecutarse en otro hilo separado, datos puede que no tenga el resultado cuando llegue a la línea donde se imprimen dichos datos:
Flutter dart async 1.JPG
Se puede ver como 'datos' no vale la lista de imágenes
  • Para solucionar el problema podemos pensar en usar el 'método then', ya que éste sólo se llama cuando el Future devuelve los datos, pero entonces estaríamos obligados a tener toda la lógica del programa que tenga que ver con los datos recibidos, dentro del then como vimos en los ejemplos anteriores...
void main() {

  print('Inicia programa'); 
  List<String>? datos;
  
  getImages('imagenes/animales').then((datos) {
      print(datos);    
  });
      
  print('Fin programa');
  
}


  • Por lo tanto, en algunos casos puede ser necesario que el hilo principal 'espere' hasta que el Future devueva el resultado.
Para lograr esto debemos:
  • Llamar al future con la palabra clave await antes del nombre de la función.
  • Definir la función desde donde se está llamando como async.


void main() async {   // Definimos como async ya que va a hacer una llamada con await a un Future

  print('Inicia programa'); 
  
  final datos = await getImages('imagenes/animales');  // Esperamos a que termine de ejecutarse.
      
  print('Datos obtenidos después de ejecutar el Future:$datos');  
  print('Fin programa');
  
}

Future<List<String>> getImages(String path)  {
  
  List<String> imagenes = ['1.jpg','2.jpg','3.jpg'];
  
  // Búsqueda de imágenes a partir del path
  
  
  return Future.delayed(Duration(seconds: 3),() => imagenes);
  
}


Ejercicios propuestos

Ejercicio 1: Sabrías aplicar los conceptos aprendidos para que el pedido apareza correctamente (ejercicio obtenido de este enlace):

String createOrderMessage() {
  var order = fetchUserOrder();
  return 'Tu pedido es: $order';
}

Future<String> fetchUserOrder() =>
    // Imagine that this function is
    // more complex and slow.
    Future.delayed(
      const Duration(seconds: 2),
      () => 'Café largo',
    );

void main() {
  print('Preparando el pedido...');
  print(createOrderMessage());
}


Ejercicio 2: Se necesita mostrar cual es el premio que recibe un alumno en base a la edad que tenga.

Crea un método obtenerEdad al que se le envíe un nombre y busque en un mapa, la edad que se corresponde con dicho nombre. Si el nombre no existe en el mapa, debe devolver el valor 0.
Crea un método de nombre obtenerPremio al que se le envia una edad y devuelve en base a una lista, el premio que le corresponde a esa edad. Utiliza el operador 'resto' para dividir la edad entre el número de regalos y que siempre devuelve un número dentro del rango de la lista.
Desde la función main, llama a obtenerEdad y el resultado empléalo para llamar a obtenerPremio y muestra el premio.


int obtenerEdad(String nombre){
  
  // ACCEDEMOS A INTERNET A BUSCAR LA EDAD
  final alumnos = {'pedro' : 27, 'ana' : 35, 'luisa' : 22};
  
  if (!alumnos.containsKey(nombre)) return 0;
  
  return alumnos[nombre]!;
}

String obtenerPremio(int edad){
  
  // ACCEDEMOS A INTERNET A BUSCAR LOS PREMIOS
  final premios = ['Jamón', 'Coche','Piso'];
  
  return premios[edad%3];
  
}

void main() {
  
  int edad = obtenerEdad('pedro');
  String premio = obtenerPremio(edad);
  
  print('Pedro con la edad de $edad, tiene de premio:$premio');
   

}


  • Ahora supón que los dos métodos acceden a Internet y son ejecutados en un hilo separado. Haz lo necesario para que todo siga funcionando igual.
Haz que el método obtenerEdad tarde 4 segundos en ejecutarse y que el método obtenerPremio tarde 2 segundos.
Future<int> obtenerEdad(String nombre){
  
  // ACCEDEMOS A INTERNET A BUSCAR LA EDAD
  final alumnos = {'pedro' : 27, 'ana' : 35, 'luisa' : 22};
  
  if (!alumnos.containsKey(nombre)) return Future.value(0);
  
  return Future.delayed(const Duration(seconds: 4),() { print ('Obtemmos la edad...');
                                                        return alumnos[nombre]!;});

}

Future<String> obtenerPremio(int edad){
  
  // ACCEDEMOS A INTERNET A BUSCAR LOS PREMIOS
  final premios = ['Jamón', 'Coche','Piso'];
  
  return Future.delayed(const Duration(seconds: 2),(){ print('Obtemos el premio...');
                                                       return premios[edad%3]; });

}

void main() async {
  
  int edad = await obtenerEdad('pedro');
  String premio = await obtenerPremio(edad);
  
  print('Pedro con la edad de $edad, tiene de premio:$premio');
  
}





Enlace a la página principal de la UD2

Enlace a la página principal del curso




-- Ángel D. Fernández González -- (2021).