diegoRodicio

Está documentación está a túa disposición sin ningún custo económico. Sen embargo, para a súa elaboración dedico moito tempo e recursos, polo que agradecería unha colaboración co que consideres oportuno. Gracias.

View Categories

🔄 Concorrencia

Dart permite escribir programas que executan varias tarefas ao mesmo tempo sen bloquear a interface de usuario nin deter a execución do resto do código. Isto é especialmente útil en aplicacións que acceden a redes, ficheiros, bases de datos ou realizan operacións lentas.

🔹 Programación asíncrona con async e await #

A programación asíncrona permite que unha parte do teu código se execute sen bloquear a execución do resto do programa. Isto é fundamental para operacións de E/S (entrada/saída), como buscar datos dunha API ou gardar nunha base de datos, onde o programa esperaría moito tempo polo resultado.

  • async: Marca unha función como asíncrona, o que significa que pode conter operacións que non se completan de inmediato. Unha función async sempre devolve un Future (ver apartado seguinte)
  • await: Só se pode usar dentro dunha función async. Detén temporalmente a execución desa función asíncrona ata que a Future á que está esperando se complete (é dicir, que devolva un valor ou un erro). Mentres espera, o fío principal de Dart pode executar outras tarefas, evitando así o bloqueo da UI.

🧪 Exemplo: #

🔗 Ver o código en DartPad

🧪 Outro exemplo: #

Ao executar este código, verás que “Continuando con outras tarefas…” e as “Tarefa síncrona” se imprimen despois de que a descarga se complete, pero o programa non se conxela durante os 3 segundos de espera. O await garante que resultado teña o valor antes de continuar, pero a natureza async da función permite que Dart faga outras cousas.

🔗 Ver o código en DartPad

🔸 Future #

Representa un resultado dunha operación asíncrona que se completará nalgún momento no futuro (ben con un valor ou con un erro). É para un único evento (unha única resposta).

Future<String> obterSaudo() async {
  await Future.delayed(Duration(seconds: 1));
  return 'Ola desde o futuro!';
}

void main() async {
  String saudo = await obterSaudo();
  print(saudo);
}

Podes encadear Futures usando .then() para especificar que facer cando o Future se completa, ou .catchError() para xestionar erros. Con async e await, o manexo faise máis lineal e lexible, semellante ao código síncrono.

🔧 Métodos máis usados de Future #

MétodoDescriciónExemplo
then()Executa unha función cando o Future se resolve correctamentefuturo.then((valor) => print(valor));
catchError()Xestiona erros se o Future fallafuturo.catchError((e) => print('Erro: $e'));
whenComplete()Executa código ao final, tanto se tivo éxito como errofuturo.whenComplete(() => print('Feito'));
Future.delayed()Crea un Future que remata logo dun atrasoFuture.delayed(Duration(seconds: 2))
Future.value(valor)Crea un Future xa resolto co valor dadoFuture.value('Rematado')
Future.error(erro)Crea un Future xa falladoFuture.error('Algo fallou')

🧪 Exemplo: #

🔗 Ver o código en DartPad

🔸 Stream #

Representa unha secuencia de eventos asíncronos. A diferenza de Future (que é para un só valor), un Stream pode emitir cero ou máis eventos ao longo do tempo. É útil para datos que chegan continuamente, como eventos de interface de usuario, datos dun socket web, ou cambios nunha base de datos.

Podes “escoitar” un Stream para reaccionar a cada evento a medida que chega.

Stream<int> contarAtraso() async* {
  for (int i = 3; i > 0; i--) {
    await Future.delayed(Duration(seconds: 1));
    yield i;
  }
}

void main() {
  contarAtraso().listen((valor) {
    print('⏳ $valor');
  });
}

🧪 Outro exemplo: #

🔗 Ver o código en DartPad

🔹 Isolates #

Dart é un linguaxe de fío único (single-threaded). Isto significa que só executa unha operación á vez no seu “fío principal” (tamén coñecido como “event loop”). async e await axúdanche a non bloquear ese fío, pero non executan código en paralelo.

Para tarefas que son computacionalmente intensivas e que poderían bloquear o fío principal (mesmo se son asíncronas), Dart ofrece Isolates. Un Isolate é como un pequeno espazo de memoria illado que ten o seu propio fío de execución e o seu propio event loop. Os Isolates non comparten memoria, polo que a comunicación entre eles faise enviando mensaxes (copias de datos), o que evita problemas de concorrencia como os deadlocks.

Usar Isolates é máis complexo e normalmente só se necesita para tarefas moi pesadas que non implican E/S, como procesamento de imaxes, cálculos matemáticos complexos, ou análise de datos grandes.

Un isolate é un fío separado con memoria propia.

🧪 Exemplo sinxelo: #

import 'dart:isolate'; // Para traballar con Isolates

// Función que se executará no novo Isolate
void _tarefaIntensiva(SendPort portoEnvio) {
  int suma = 0;
  for (int i = 0; i < 1000000000; i++) {
    suma += i;
  }
  portoEnvio.send(suma); // Envía o resultado de volta ao Isolate principal
}

Future<void> executarTarefaConIsolate() async {
  print('Inicio da tarefa principal.');

  ReceivePort portoRecepcion = ReceivePort(); // Porto para recibir mensaxes do Isolate

  // Crea un novo Isolate e asocialle a función e o porto de envío
  Isolate isolate = await Isolate.spawn(_tarefaIntensiva, portoRecepcion.sendPort);

  print('Isolate lanzado. O fío principal non está bloqueado.');

  // Escoita as mensaxes do Isolate
  portoRecepcion.listen((resultado) {
    print('Resultado da tarefa intensiva no Isolate: $resultado');
    isolate.kill(); // Mata o Isolate despois de obter o resultado
    print('Isolate finalizado.');
  });

  print('O fío principal continúa facendo outras cousas.');
  for (int i = 0; i < 5; i++) {
    print('Fío principal: $i');
  }
}

void main() {
  executarTarefaConIsolate();
}

🔗 Para saber máis #

Se queres afondar na concorrencia e programación asíncrona en Dart, podes consultar:

📘 Concurrencia avanzada →