Skip to content

Asíncronismo

Dart desde concepción ha sido un lenguaje de un solo hilo, pero tiene un stack de ejecución que permite que sea asíncrono (si vienes de javascript trabaja igual).

Es decir, en ocasiones se deben realizar acciones que se pueden tardar un tiempo corto o largo, esto puede variar en funcion de la tarea, pero nuestro flujo de la ampliación no puede detenerse, este debe continuar. Entonces ahi es donde usamos la programación asíncrona, significa que la tarea se manda a segundo plano (background) mientras el flujo principal continua, y cuando dicha tarea termina continua con lo que debe hacer. Con todo esto logramos que nuestro programa no se atore y el usuario pueda seguir interactuando con la aplicación.

Tenemos tres maneras de utilizar la programación asíncrona:

  • callback: Pasamos una funcion que se conoce como callback, que se ejecuta hasta que la funcion a la cual se le paso termina y le paso los parámetros que necesita para continuar con su tarea.
  • then con Future: una funcion que se llama para encadenar varios callback, sin la necesidad de caer en el callback hell
  • Async-await con Future: De esta manera es la mas elegante de "convertir" nuestro condigo asíncrono en síncrono; sigue siendo asíncrono pero visualmente se viera síncrono.

En esta sección se hace uso intensivo de funciones anónimas y funciones flechas, esto es lo mas normal y común en programación asíncrona.

Callback

Los callback son la manera mas tradicional de manejar eventos o escuchadores. Un callback es una función que se pasa a otra función, por eso le llamamos función callback o para abreviar cb.

Declaramos una funcion que recibe 3 parámetros, dos valores enteros y una funcion callback, la cual se ejecuta después de que se realicen ciertas acciones y devuelve la información procesada.

// programación asíncrona
void calculo(int a, int b, void Function(int a, int b) callback) {
  print("el primer valor es $a");
  print("el primer valor es $b");
  int doble1 = a * 2;
  int doble2 = b * 2;

  callback(doble1, doble2);
}

void main(List<String> args) {
  calculo(5, 2, (a, b) {
    print("suma ${a + b}");
  });
  calculo(5, 2, (a, b) {
    print("resta ${a - b}");
  });
  calculo(5, 2, (a, b) {
    print("multi ${a * b}");
  });
  calculo(5, 2, (a, b) {
    print("divide ${a / b}");
  });
}

La formas mas común y usual es cuando usamos la funcion forEach de las colecciones, para recorrer el array y realizar una acción.

main(List<String> args) {
  var lista = ["lua", "dart", "js", "java", "python"];

  lista.forEach((lenguaje) {
    //funcion anónima
    print("Es un lenguaje de programación $lenguaje");
    print("=========================================");
  });

  //funcion flecha
  lista.forEach((e) => print("Es un lenguaje de programación $e"));
}

Future

El objeto Future es para indicar que lo que se esta devolviendo es una acción asíncrona, es decir, que no esta devolviendo el resultado, esta devolviendo un Future y cuando termine la tarea, ahora si tendremos el objeto deseado.

Para obtener el resultado del Future tenemos dos opciones:

  • then-catch
  • async-await

then-catch

Cuando recibimos como objeto un Future la forma de obtener ese valor es utilizando su método then, el cual recibe un callback para retomar ese valor y realizar la siguiente acción.

import "dart:math";

Future<int> numberUnknown() {
  int time = Random().nextInt(5) + 1;
  print("Time to wait: $time");
  return Future.delayed(Duration(seconds: time), () => Random().nextInt(100));
}

void main(List<String> args) {
  numberUnknown().then((value) {
    print("El valor recibido es: $value");
  });
}

Manejo de error catchError

Para esto tenemos una funcion llamada catchError() la cual recibe un parámetro de error si en el método then fallo.

import "dart:math";

Future<int> numberUnknown() {
  int time = Random().nextInt(5) + 1;
  print("Time to wait: $time");
  return Future.delayed(Duration(seconds: time), () => Random().nextInt(100));
}

void main(List<String> args) {
  numberUnknown().then((value) {
    print(value);
  }).catchError((e) {
    print("The error was: $e");
  });
}

async-await

La segunda opción que es mas elegante y se prefiere utilizar es con las palabras reservadas async con await.

Note

Para poder usar await dentro de una funcion debe ser async.

La forma de utilizar await es colocándole a la funcion que es asíncrona y con esto ya no se necesita usar su método then. Y donde se realizaría el then, se antepone await y recibimos el valor como si fuera código síncrono.

void nameFunction() async{
    int value = await functionAsync();
}
Realizamos el código anterior cambiando el método then por async-await

import "dart:math";

Future<int> numberUnknown() {
  int time = Random().nextInt(5) + 1;
  print("Time to wait: $time");
  return Future.delayed(Duration(seconds: time), () => Random().nextInt(100));
}

void main(List<String> args) async {
  int numero = await numberUnknown();
  print("El valor recibido es: $numero");
}

Manejo de error try-catch

Con async-await se trata con un bloque try-catch, dado que se vuelve un estilo de código síncrono.

import "dart:math";

Future<int> numberUnknown() {
  int time = Random().nextInt(5) + 1;
  print("Time to wait: $time");
  return Future.delayed(Duration(seconds: time), () => Random().nextInt(100));
}

void main(List<String> args) async {
  try {
    int numero = await numberUnknown();
    print("El valor recibido es: $numero");
  } catch (e) {
    print("El error fue: $e");
  }
}