dart:async
Aprende sobre las características principales de la biblioteca dart:async de Dart.
La programación asíncrona a menudo utiliza funciones callback, pero Dart proporciona alternativas: objetos Future y Stream. Un Future es como una promesa de un resultado que se proporcionará en algún momento en el futuro. Un Stream es una forma de obtener una secuencia de valores, como eventos. Future, Stream y más están en la biblioteca dart:async (referencia de la API).
La biblioteca dart:async funciona tanto en aplicaciones web como en aplicaciones de línea de comandos. Para usarla, importa dart:async:
import 'dart:async';
Futuro
#Los objetos Future aparecen por todas las librerías de Dart, a menudo como el objeto devuelto por un método asíncrono. Cuando un future se completa, su valor está listo para usar.
Usar await
#Antes de utilizar directamente la API de Future, considera usar await en su lugar.
El código que utiliza expresiones await puede ser más fácil de entender
que el código que utiliza la API de Future.
Considera la siguiente función. Utiliza el método then() de Future
para ejecutar tres funciones asíncronas consecutivamente,
esperando que cada una se complete antes de ejecutar la siguiente.
void runUsingFuture() {
// ...
findEntryPoint()
.then((entryPoint) {
return runExecutable(entryPoint, args);
})
.then(flushThenExit);
}
El código equivalente con expresiones await se parece más al código síncrono:
Future<void> runUsingAsyncAwait() async {
// ...
var entryPoint = await findEntryPoint();
var exitCode = await runExecutable(entryPoint, args);
await flushThenExit(exitCode);
}
Una función async puede capturar excepciones de los Futures.
Por ejemplo:
var entryPoint = await findEntryPoint();
try {
var exitCode = await runExecutable(entryPoint, args);
await flushThenExit(exitCode);
} catch (e) {
// Handle the error...
}
Para obtener más información sobre el uso de await y las características relacionadas del lenguaje Dart,
consulta el tutorial de programación asíncrona.
Uso básico
#Puedes usar then() para programar código que se ejecute cuando el future se complete. Por
ejemplo, Client.read()
devuelve un Future, ya que las solicitudes HTTP
pueden tardar un tiempo. Usar then() te permite ejecutar algún código cuando ese Future
se haya completado y el valor de cadena prometido esté disponible:
httpClient.read(url).then((String result) {
print(result);
});
Usa catchError() para manejar cualquier error o excepción que un objeto Future
pueda lanzar.
httpClient
.read(url)
.then((String result) {
print(result);
})
.catchError((e) {
// Handle or ignore the error.
});
El patrón then().catchError() es la versión asíncrona de
try-catch.
Encadenar múltiples métodos asíncronos
#El método then() devuelve un Future, proporcionando una forma útil de ejecutar
múltiples funciones asíncronas en un orden determinado.
Si el callback registrado con then() devuelve un Future,
then() devuelve un Future que se completará
con el mismo resultado que el Future devuelto por el callback.
Si el callback devuelve un valor de cualquier otro tipo,
then() crea un nuevo Future que se completa con dicho valor.
Future result = costlyQuery(url);
result
.then((value) => expensiveWork(value))
.then((_) => lengthyComputation())
.then((_) => print('Done!'))
.catchError((exception) {
/* Handle exception... */
});
En el ejemplo anterior, los métodos se ejecutan en el siguiente orden:
-
costlyQuery() -
expensiveWork() -
lengthyComputation()
Aquí está el mismo código escrito usando await:
try {
final value = await costlyQuery(url);
await expensiveWork(value);
await lengthyComputation();
print('Done!');
} catch (e) {
/* Handle exception... */
}
Esperar múltiples futures
#A veces, tu algoritmo necesita invocar muchas funciones asíncronas y esperar a que todas se completen antes de continuar. Usa el método estático Future.wait() para administrar múltiples Futures y esperar a que se completen:
Future<void> deleteLotsOfFiles() async => ...
Future<void> copyLotsOfFiles() async => ...
Future<void> checksumLotsOfOtherFiles() async => ...
await Future.wait([
deleteLotsOfFiles(),
copyLotsOfFiles(),
checksumLotsOfOtherFiles(),
]);
print('Done with all the long steps!');
Future.wait() devuelve un future que se completa una vez que todos los futures
proporcionados se han completado. Se completa ya sea con sus resultados,
o con un error si alguno de los futures proporcionados falla.
Manejo de errores para múltiples futures
#También puedes esperar operaciones en paralelo en:
Estas extensiones devuelven un Future con los
valores resultantes de todos los futures proporcionados.
A diferencia de Future.wait, también te permiten manejar errores.
Si cualquier future de la colección se completa con un error,
wait se completa con un ParallelWaitError.
Esto permite al llamador manejar errores individuales y
descartar los resultados exitosos si es necesario.
Cuando no necesitas los valores resultantes de cada future,
usa wait en un iterable de futures:
Future<int> delete() async => ...;
Future<String> copy() async => ...;
Future<bool> errorResult() async => ...;
void main() async {
try {
// Wait for each future in a list, returns a list of futures:
var results = await [delete(), copy(), errorResult()].wait;
} on ParallelWaitError<List<bool?>, List<AsyncError?>> catch (e) {
print(e.values[0]); // Prints successful future
print(e.values[1]); // Prints successful future
print(e.values[2]); // Prints null when the result is an error
print(e.errors[0]); // Prints null when the result is successful
print(e.errors[1]); // Prints null when the result is successful
print(e.errors[2]); // Prints error
}
}
Cuando sí necesitas los valores de resultados individuales de cada future,
usa wait en un record de futures.
Esto proporciona la ventaja adicional de que los futures pueden ser de diferentes tipos:
Future<int> delete() async => ...;
Future<String> copy() async => ...;
Future<bool> errorResult() async => ...;
void main() async {
try {
// Wait for each future in a record.
// Returns a record of futures that you can destructure.
final (deleteInt, copyString, errorBool) =
await (delete(), copy(), errorResult()).wait;
// Do something with the results...
} on ParallelWaitError<
(int?, String?, bool?),
(AsyncError?, AsyncError?, AsyncError?)
> catch (e) {
// ...
}
}
Stream
#Los objetos Stream aparecen en las API de Dart, representando secuencias de datos. Por ejemplo, los eventos HTML como los clics en botones se entregan utilizando streams. También puedes leer un archivo como un stream.
Usar un bucle for asíncrono
#A veces puedes usar un bucle for asíncrono (await for)
en lugar de usar la API de Stream.
Considera la siguiente función.
Utiliza el método listen() de Stream
para suscribirse a una lista de archivos,
pasando una función literal que busca en cada archivo o directorio.
void main(List<String> arguments) {
// ...
FileSystemEntity.isDirectory(searchPath).then((isDir) {
if (isDir) {
final startingDir = Directory(searchPath);
startingDir.list().listen((entity) {
if (entity is File) {
searchFile(entity, searchTerms);
}
});
} else {
searchFile(File(searchPath), searchTerms);
}
});
}
El código equivalente con expresiones await,
incluyendo un bucle for asíncrono (await for),
se parece más al código síncrono:
void main(List<String> arguments) async {
// ...
if (await FileSystemEntity.isDirectory(searchPath)) {
final startingDir = Directory(searchPath);
await for (final entity in startingDir.list()) {
if (entity is File) {
searchFile(entity, searchTerms);
}
}
} else {
searchFile(File(searchPath), searchTerms);
}
}
Para obtener más información sobre el uso de await y las características
relacionadas del lenguaje Dart, consulta el
tutorial de programación asíncrona.
Escuchar datos de un stream
#Para obtener cada valor a medida que llega, usa await for o
suscríbete al stream utilizando el método listen():
// Add an event handler to a button.
submitButton.onClick.listen((e) {
// When the button is clicked, it runs this code.
submitData();
});
En este ejemplo, la propiedad onClick es un objeto Stream proporcionado por
el botón de envío.
Si solo te importa un evento, puedes obtenerlo usando una propiedad como
first, last o single. Para probar el evento antes de manejarlo,
usa un método como firstWhere(), lastWhere() o singleWhere().
Si te importa un subconjunto de eventos, puedes usar métodos como
skip(), skipWhile(), take(), takeWhile() y
where().
Transformar datos de un stream
#A menudo, necesitas cambiar el formato de los datos de un stream antes de poder
utilizarlos. Usa el método transform() para producir un stream con un
tipo de datos diferente:
var lines = inputStream
.transform(utf8.decoder)
.transform(const LineSplitter());
Este ejemplo utiliza dos transformadores. Primero usa utf8.decoder para transformar el stream de enteros en un stream de cadenas. Luego usa un LineSplitter para transformar el stream de cadenas en un stream de líneas separadas. Estos transformadores son de la biblioteca dart:convert (consulta la sección de dart:convert).
Manejo de errores y finalización
#La forma de especificar el código de manejo de errores y de finalización
depende de si utilizas un bucle for asíncrono (await for)
o la API de Stream.
Si utilizas un bucle for asíncrono, usa try-catch para manejar los errores. El código que se ejecuta después de cerrar el stream va después del bucle for asíncrono.
Future<void> readFileAwaitFor() async {
var config = File('config.txt');
Stream<List<int>> inputStream = config.openRead();
var lines = inputStream
.transform(utf8.decoder)
.transform(const LineSplitter());
try {
await for (final line in lines) {
print('Got ${line.length} characters from stream');
}
print('file is now closed');
} catch (e) {
print(e);
}
}
Si utilizas la API de Stream,
maneja los errores registrando un oyente onError.
Ejecuta código después de cerrar el stream registrando
un oyente onDone.
var config = File('config.txt');
Stream<List<int>> inputStream = config.openRead();
inputStream
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen(
(String line) {
print('Got ${line.length} characters from stream');
},
onDone: () {
print('file is now closed');
},
onError: (e) {
print(e);
},
);
Más información
#Para ver algunos ejemplos de uso de Future y Stream en aplicaciones de línea de comandos, consulta la documentación de dart:io. Consulta también estos artículos y tutoriales:
A menos que se indique lo contrario, la documentación en este sitio refleja Dart 3.12.2. Página actualizada por última vez el 2025-05-05. Ver fuente oreportar un problema.