Programación asíncrona: futures, async, await
¡Aprende y practica escribiendo código asíncrono en DartPad!
Este tutorial te enseña cómo escribir código asíncrono usando
futures y las palabras clave async y await.
Usando editores DartPad incrustados,
puedes probar tus conocimientos ejecutando código de ejemplo
y completando ejercicios.
Para aprovechar al máximo este tutorial, deberías tener lo siguiente:
- Conocimiento de la sintaxis básica de Dart.
- Algo de experiencia escribiendo código asíncrono en otro lenguaje.
- Tener habilitadas las linter rules
discarded_futuresyunawaited_futures.
Este tutorial cubre el siguiente material:
- Cómo y cuándo usar las palabras clave
asyncyawait. - Cómo afecta el uso de
asyncyawaital orden de ejecución. - Cómo manejar errores de una llamada asíncrona
usando expresiones
try-catchen funcionesasync.
Tiempo estimado para completar este tutorial: 40-60 minutos.
Los ejercicios de este tutorial tienen fragmentos de código parcialmente completados.
Puedes usar DartPad para probar tus conocimientos completando el código y
haciendo clic en el botón Run.
No edites el código de prueba en la función main o debajo.
Si necesitas ayuda, expande el menú desplegable Pista o Solución después de cada ejercicio.
Por qué es importante el código asíncrono
#Las operaciones asíncronas permiten que tu programa complete el trabajo mientras espera que termine otra operación. Aquí tienes algunas operaciones asíncronas comunes:
- Obtener datos a través de una red.
- Escribir en una base de datos.
- Leer datos de un archivo.
Estas operaciones asíncronas usualmente proporcionan su resultado como un Future
o, si el resultado tiene múltiples partes, como un Stream.
Estos cálculos introducen asincronía en un programa.
Para acomodar esa asincronía inicial,
otras funciones normales de Dart también necesitan volverse asíncronas.
Para interactuar con estos resultados asíncronos,
puedes usar las palabras clave async y await.
La mayoría de las funciones asíncronas son simplemente funciones de Dart asíncronas
que dependen, posiblemente en el fondo,
de un cálculo intrínsecamente asíncrono.
Ejemplo: Uso incorrecto de una función asíncrona
#El siguiente ejemplo muestra la forma incorrecta
de usar una función asíncrona (fetchUserOrder()).
Más adelante corregirás el ejemplo usando async y await.
Antes de ejecutar este ejemplo, intenta detectar el problema:
¿cuál crees que será la salida?
He aquí por qué el ejemplo no logra imprimir el valor
que fetchUserOrder() eventualmente produce:
fetchUserOrder()es una función asíncrona que, después de un retraso, proporciona una cadena que describe el pedido del usuario: un "Large Latte".- Para obtener el pedido del usuario,
createOrderMessage()debería llamar afetchUserOrder()y esperar a que termine. ComocreateOrderMessage()no espera a quefetchUserOrder()termine,createOrderMessage()no logra obtener el valor de cadena quefetchUserOrder()eventualmente proporciona. - En su lugar,
createOrderMessage()obtiene una representación del trabajo pendiente por hacer: un future incompleto. Aprenderás más sobre los futures en la siguiente sección. - Debido a que
createOrderMessage()no logra obtener el valor que describe el pedido del usuario, el ejemplo no imprime "Large Latte" en la consola, y en su lugar imprime "Your order is: Instance of '_Future<String>'".
En las siguientes secciones aprenderás sobre los futures y sobre cómo trabajar con ellos
(usando async y await)
para que puedas escribir el código necesario para hacer que fetchUserOrder()
imprima el valor deseado ("Large Latte") en la consola.
¿Qué es un future?
#Un future (con "f" minúscula) es una instancia de la clase Future (con "F" mayúscula). Un future representa el resultado de una operación asíncrona y puede tener dos estados: incompleto o completado.
Incompleto (Uncompleted)
#Cuando llamas a una función asíncrona, esta devuelve un future incompleto. Ese future está esperando a que termine la operación asíncrona de la función o a que lance un error.
Completado (Completed)
#Si la operación asíncrona tiene éxito, el future se completa con un valor. De lo contrario, se completa con un error.
Completar con un valor
#Un future de tipo Future<T> se completa con un valor de tipo T.
Por ejemplo, un future de tipo Future<String> produce un valor de cadena.
Si un future no produce un valor utilizable,
entonces el tipo del future es Future<void>.
Completar con un error
#Si la operación asíncrona realizada por la función falla por cualquier motivo, el future se completa con un error.
Ejemplo: Presentando los futures
#En el siguiente ejemplo, fetchUserOrder() devuelve un future
que se completa después de imprimir en la consola.
Debido a que no devuelve un valor utilizable,
fetchUserOrder() tiene el tipo Future<void>.
Antes de ejecutar el ejemplo,
intenta predecir qué se imprimirá primero:
"Large Latte" o "Fetching user order...".
En el ejemplo anterior,
a pesar de que fetchUserOrder() se ejecuta antes de la llamada a print()
en la línea 8,
la consola muestra la salida de la línea 8 ("Fetching user order...")
antes de la salida de fetchUserOrder() ("Large Latte").
Esto se debe a que fetchUserOrder() tiene un retraso antes de imprimir "Large Latte".
Ejemplo: Completar con un error
#Ejecuta el siguiente ejemplo para ver cómo se completa un future con un error. Un poco más adelante aprenderás a manejar el error.
En este ejemplo, fetchUserOrder() se completa
con un error que indica que el ID de usuario no es válido.
Has aprendido sobre los futures y cómo se completan,
pero ¿cómo usas los resultados de las funciones asíncronas?
En la siguiente sección aprenderás cómo obtener resultados
con las palabras clave async y await.
Trabajar con futures: async y await
#Las palabras clave async y await proporcionan una forma declarativa
de definir funciones asíncronas y usar sus resultados.
Recuerda estas dos pautas básicas cuando uses async y await:
- Para definir una función async, añade
asyncantes del cuerpo de la función: - The
awaitkeyword works only inasyncfunctions.
Aquí hay un ejemplo que convierte main()
de una función síncrona a una asíncrona.
Primero, añade la palabra clave async antes del cuerpo de la función:
void main() async { ··· }
Si la función tiene un tipo de retorno declarado,
entonces actualiza el tipo para que sea Future<T>,
where T es el tipo del valor que devuelve la función.
Si la función no devuelve explícitamente un valor,
entonces el tipo de retorno es Future<void>:
Future<void> main() async { ··· }
Ahora que tienes una función async,
puedes usar la palabra clave await para esperar a que se complete un future:
print(await createOrderMessage());
Como muestran los siguientes dos ejemplos, las palabras clave async y await
dan como resultado un código asíncrono que se parece mucho al código síncrono.
Las únicas diferencias se destacan en el ejemplo asíncrono,
que, si tu ventana es lo suficientemente ancha, está
a la derecha del ejemplo síncrono.
Ejemplo: funciones síncronas
#String createOrderMessage() {
var order = fetchUserOrder();
return 'Your order is: $order';
}
Future<String> fetchUserOrder() =>
// Imagine that this function is
// more complex and slow.
Future.delayed(const Duration(seconds: 2), () => 'Large Latte');
void main() {
print('Fetching user order...');
print(createOrderMessage());
}
Fetching user order...
Your order is: Instance of 'Future<String>'
Como se muestra en los siguientes dos ejemplos, funciona como código síncrono.
Ejemplo: funciones asíncronas
#Future<String> createOrderMessage() async {
var order = await fetchUserOrder();
return 'Your order is: $order';
}
Future<String> fetchUserOrder() =>
// Imagine that this function is
// more complex and slow.
Future.delayed(const Duration(seconds: 2), () => 'Large Latte');
Future<void> main() async {
print('Fetching user order...');
print(await createOrderMessage());
}
Fetching user order...
Your order is: Large Latte
El ejemplo asíncrono es diferente en tres aspectos:
- El tipo de retorno de
createOrderMessage()cambia deStringaFuture<String>. - La palabra clave
asyncaparece antes del cuerpo de las funciones paracreateOrderMessage()ymain(). - La palabra clave
awaitaparece antes de llamar a las funciones asíncronasfetchUserOrder()ycreateOrderMessage().
Flujo de ejecución con async y await
#Una función async se ejecuta sincrónicamente hasta la primera palabra clave await.
Esto significa que dentro del cuerpo de una función async,
todo el código síncrono antes de la primera palabra clave await se ejecuta inmediatamente.
Ejemplo: Ejecución dentro de funciones async
#Ejecuta el siguiente ejemplo para ver cómo procede la ejecución
dentro del cuerpo de una función async.
¿Cuál crees que será la salida?
Después de ejecutar el código en el ejemplo anterior, intenta invertir las líneas 2 y 3:
var order = await fetchUserOrder();
print('Awaiting user order...');
Observa que el orden temporal de la salida cambia, ahora que print('Awaiting user order')
aparece después de la primera palabra clave await en printOrderMessage().
Ejercicio: Practicar el uso de async y await
#El siguiente ejercicio es una prueba unitaria fallida
que contiene fragmentos de código parcialmente completados.
Tu tarea es completar el ejercicio escribiendo código para que las pruebas pasen.
No necesitas implementar main().
Para simular operaciones asíncronas, llama a las siguientes funciones, que se te proporcionan:
| Function | Firma de tipo | Descripción |
|---|---|---|
| fetchRole() | Future<String> fetchRole() |
Obtiene una breve descripción del rol del usuario. |
| fetchLoginAmount() | Future<int> fetchLoginAmount() |
Obtiene el número de veces que un usuario ha iniciado sesión. |
Parte 1: reportUserRole()
#
Agrega código a la función reportUserRole() para que haga lo siguiente:
-
Returns a future that completes with the following
string:
"User role: <user role>"- Nota: Debes usar el valor real devuelto por
fetchRole(); copiar y pegar el valor de retorno de ejemplo no hará que la prueba pase. - Valor de retorno de ejemplo:
"User role: tester"
- Nota: Debes usar el valor real devuelto por
- Obtiene el rol del usuario llamando a la función proporcionada
fetchRole().
Parte 2: reportLogins()
#
Implementa una función async reportLogins() para que haga lo siguiente:
-
Returns the string
"Total number of logins: <# of logins>".- Nota: Debes usar el valor real devuelto por
fetchLoginAmount(); copiar y pegar el valor de retorno de ejemplo no hará que la prueba pase. - Valor de retorno de ejemplo de
reportLogins():"Total number of logins: 57"
- Nota: Debes usar el valor real devuelto por
- Obtiene el número de inicios de sesión llamando a la función proporcionada
fetchLoginAmount().
Pista
¿Te acordaste de agregar la palabra clave async a la función reportUserRole?
¿Te acordaste de usar la palabra clave await antes de invocar a fetchRole()?
Recuerda: reportUserRole necesita devolver un Future.
Solución
Future<String> reportUserRole() async {
final username = await fetchRole();
return 'User role: $username';
}
Future<String> reportLogins() async {
final logins = await fetchLoginAmount();
return 'Total number of logins: $logins';
}
Manejo de errores
#Para manejar errores en una función async, usa try-catch:
try {
print('Awaiting user order...');
var order = await fetchUserOrder();
} catch (err) {
print('Caught error: $err');
}
Dentro de una función async, puedes escribir
cláusulas try-catch
de la misma manera que lo harías en el código síncrono.
Ejemplo: async y await con try-catch
#Ejecuta el siguiente ejemplo para ver cómo manejar un error de una función asíncrona. ¿Cuál crees que será la salida?
Ejercicio: Practicar el manejo de errores
#El siguiente ejercicio proporciona práctica para manejar errores con código asíncrono, utilizando el enfoque descrito en la sección anterior. Para simular operaciones asíncronas, tu código llamará a la siguiente función, que se te proporciona:
| Function | Firma de tipo | Descripción |
|---|---|---|
| fetchNewUsername() | Future<String> fetchNewUsername() |
Devuelve el nuevo nombre de usuario que puedes usar para reemplazar uno antiguo. |
Usa async y await para implementar una función asíncrona changeUsername()
función
que haga lo siguiente:
-
Calls the provided asynchronous function
fetchNewUsername()and returns its result.- Valor de retorno de ejemplo de
changeUsername():"jane_smith_92"
- Valor de retorno de ejemplo de
-
Catches any error that occurs and returns the string value of the error.
- Puedes usar el método toString() para convertir en cadena de texto tanto las Exceptions y Errors.
Pista
Implementa changeUsername para devolver la cadena de fetchNewUsername o,
si eso falla, el valor de cadena de cualquier error que ocurra.
Recuerda: Puedes usar una sentencia try-catch para capturar y manejar errores.
Solución
Future<String> changeUsername() async {
try {
return await fetchNewUsername();
} catch (err) {
return err.toString();
}
}
Ejercicio: Poniéndolo todo junto
#Es hora de practicar lo que has aprendido en un último ejercicio.
Para simular operaciones asíncronas, este ejercicio proporciona las funciones asíncronas
fetchUsername() y logoutUser():
| Function | Firma de tipo | Descripción |
|---|---|---|
| fetchUsername() | Future<String> fetchUsername() |
Devuelve el nombre asociado con el usuario actual. |
| logoutUser() | Future<String> logoutUser() |
Realiza el cierre de sesión del usuario actual y devuelve el nombre de usuario que cerró la sesión. |
Escribe lo siguiente:
Parte 1: addHello()
#
- Escribe una función
addHello()que tome un único argumentoString. addHello()devuelve su argumentoStringprecedido por'Hello '.
Ejemplo:addHello('Jon')devuelve'Hello Jon'.
Parte 2: greetUser()
#
- Escribe una función
greetUser()que no tome argumentos. - Para obtener el nombre de usuario,
greetUser()llama a la función asíncrona proporcionadafetchUsername(). greetUser()crea un saludo para el usuario llamando aaddHello(), pasándole el nombre de usuario y devolviendo el resultado.
Ejemplo: SifetchUsername()devuelve'Jenny', entoncesgreetUser()devuelve'Hello Jenny'.
Parte 3: sayGoodbye()
#
-
Write a function
sayGoodbye()that does the following:- No toma argumentos.
- Captura cualquier error.
- Llama a la función asíncrona proporcionada
logoutUser().
- Si
logoutUser()falla,sayGoodbye()devuelve cualquier cadena que desees. - Si
logoutUser()tiene éxito,sayGoodbye()devuelve la cadena'<result> Thanks, see you next time', donde<result>es el valor de cadena devuelto al llamar alogoutUser().
Pista
Las funciones greetUser y sayGoodbye deben ser asíncronas,
mientras que addHello debe ser una función normal y síncrona.
Recuerda: Puedes usar una sentencia try-catch para capturar y manejar errores.
Solución
String addHello(String user) => 'Hello $user';
Future<String> greetUser() async {
final username = await fetchUsername();
return addHello(username);
}
Future<String> sayGoodbye() async {
try {
final result = await logoutUser();
return '$result Thanks, see you next time';
} catch (e) {
return 'Failed to logout user: $e';
}
}
¿Qué linter rules funcionan para futures?
#Para detectar errores comunes que surgen al trabajar con async y futures, habilita las siguientes linter rules (lints):
¿Qué sigue?
#¡Felicitaciones, has terminado el tutorial! Si deseas obtener más información, aquí hay algunas sugerencias sobre a dónde ir a continuación:
- Juega con DartPad.
-
Learn more about futures and asynchronous code in Dart:
- Tutorial de Streams: Aprende a trabajar con una secuencia de eventos asíncronos.
- Concurrencia en Dart: Comprende y aprende a implementar la concurrencia en Dart.
- Programación asíncrona: Sumérgete en el soporte de lenguaje y librería de Dart para codificación asíncrona.
- Videos de Dart de Google: Mira uno o más de los videos sobre codificación asíncrona.
- ¡Obtén el Dart SDK!
A menos que se indique lo contrario, la documentación de este sitio refleja Dart 3.12.2. Página actualizada por última vez el 02-02-2026. Ver fuente oreportar un problema.