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ónasyncsempre devolve unFuture(ver apartado seguinte)await: Só se pode usar dentro dunha funciónasync. Detén temporalmente a execución desa función asíncrona ata que aFutureá 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: #
🧪 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.
🔸 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. Conasynceawait, o manexo faise máis lineal e lexible, semellante ao código síncrono.
🔧 Métodos máis usados de Future #
| Método | Descrición | Exemplo |
|---|---|---|
then() | Executa unha función cando o Future se resolve correctamente | futuro.then((valor) => print(valor)); |
catchError() | Xestiona erros se o Future falla | futuro.catchError((e) => print('Erro: $e')); |
whenComplete() | Executa código ao final, tanto se tivo éxito como erro | futuro.whenComplete(() => print('Feito')); |
Future.delayed() | Crea un Future que remata logo dun atraso | Future.delayed(Duration(seconds: 2)) |
Future.value(valor) | Crea un Future xa resolto co valor dado | Future.value('Rematado') |
Future.error(erro) | Crea un Future xa fallado | Future.error('Algo fallou') |
🧪 Exemplo: #
🔸 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: #
🔹 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: