Programación Asíncrona en Dart: Futures, Async y Streams

  Información

La programación asíncrona permite ejecutar tareas de forma no bloqueante, mejorando la eficiencia y la capacidad de respuesta de las aplicaciones. Dart ofrece varias herramientas para manejar operaciones asíncronas, entre las que se incluyen Futures, async, await, y streams.

Futures y Manejo de Promesas

Un Future representa un valor que puede estar disponible en el futuro, ya sea porque la operación se completó con éxito o falló. Los Futures son ideales para manejar tareas que toman tiempo, como solicitudes HTTP o lecturas de archivos.

dart
Future<String> obtenerDatos() async {
// Simulando un retraso
await Future.delayed(Duration(seconds: 2));
  return 'Datos obtenidos';
}

void main() async {
  String datos = await obtenerDatos();
  print(datos); 
  // Resultado: Datos obtenidos
}

Uso de async y await

Las palabras clave async y await permiten escribir código asíncrono de manera más legible y fácil de seguir. async se utiliza para declarar que una función es asíncrona, mientras que await se usa para esperar el resultado de un Future antes de continuar.

dart
void main() async {
  print('Esperando datos...');
  String datos = await obtenerDatos();
  print(datos);
  // Resultado: Datos obtenidos
}

Streams

Los streams son una forma de manejar secuencias de datos asíncronos. Permiten recibir múltiples eventos o datos a lo largo del tiempo, como datos de un socket o actualizaciones de una base de datos.

dart
Stream<int> contarHastaTres() async* {
for (int i = 1; i <= 3; i++) {
  await Future.delayed(Duration(seconds: 1));
  yield i; // Emite el valor i
}
}

void main() async {
await for (var numero in contarHastaTres()) {
  print(numero); 
  // Resultado: 1, 2, 3 (con un segundo de espera entre cada número)
}
}

Stream API en Flutter

La Stream API en Flutter permite manejar flujos de datos asíncronos, facilitando la programación reactiva y la actualización dinámica de la interfaz de usuario. A través de los streams, las aplicaciones pueden reaccionar a eventos y datos que cambian con el tiempo, como la entrada del usuario o respuestas de servicios web.

Casos de Uso Comunes

Manejo de Eventos de Usuario

Los streams se pueden usar para gestionar eventos de usuario, como clics de botones o cambios en campos de texto. Esto permite una respuesta instantánea y eficiente en la interfaz.

dart
StreamController<String> controlador = StreamController<String>();

void main() {
controlador.stream.listen((dato) {
  print('Nuevo dato: $dato');
});

controlador.add('Evento 1');
controlador.add('Evento 2');
}

Consumo de Datos de una API

Los streams son útiles para consumir datos de APIs que envían actualizaciones periódicas. Esto es ideal para aplicaciones que necesitan mostrar información en tiempo real, como feeds de redes sociales.

dart
Stream<String> obtenerDatosDesdeAPI() async* {
// Simulando múltiples respuestas de una API
await Future.delayed(Duration(seconds: 1));
yield 'Respuesta 1';
await Future.delayed(Duration(seconds: 1));
yield 'Respuesta 2';
}

void main() async {
await for (var dato in obtenerDatosDesdeAPI()) {
  print(dato); 
  // Resultado: Respuesta 1, Respuesta 2 (con un segundo de espera entre cada respuesta)
}
}

Actualización en Tiempo Real de la Interfaz

Los streams son esenciales para actualizar la interfaz de usuario en tiempo real, como en aplicaciones de chat donde los mensajes nuevos se reciben y muestran inmediatamente.

dart
class ChatScreen extends StatelessWidget {
final Stream<String> mensajes = Stream.periodic(Duration(seconds: 1), (count) => 'Mensaje $count');


Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('Chat')),
    body: StreamBuilder<String>(
      stream: mensajes,
      builder: (context, snapshot) {
        return ListTile(title: Text(snapshot.data ?? 'Esperando mensajes...'));
      },
    ),
  );
}
}