Learn Rust Coding · Lección

Trabajo con Futures y tareas

Comprenda el trait `Future` y cómo se planifican y gestionan las tareas dentro de un entorno de ejecución asíncrono.

Lección 3 de 311 pasos

Trabajo con Futures y tareas es una lección gratuita de Learn Rust Coding en CoddyKit. Esta es la lección 3 de 3. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Learn Rust Coding, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Learn Rust Coding incluye 3 lecciones en total.

¿Qué es un Future de Rust?

En Rust asíncrono, un Future es un trait que representa un cálculo asíncrono que puede completarse en algún momento. Piense en él como una promesa de un valor que todavía no está listo.

  • Es el bloque de construcción fundamental de Rust asíncrono.
  • Las async fn de Rust en realidad devuelven un tipo anónimo que implementa el trait Future.
  • El valor no se calcula inmediatamente; se calcula cuando un ejecutor hace «poll» del Future.

Cómo avanzan los Futures

Un Future no se ejecuta por sí solo. Un ejecutor (como el runtime de Tokio) hace «poll» del mismo repetidamente para comprobar si ha avanzado o se ha completado.

  • Al hacer «poll», un Future devuelve Poll::Pending (todavía no ha terminado) o Poll::Ready(T) (ha terminado; aquí está el resultado).
  • Si devuelve Pending, el ejecutor sabe que debe volver a hacer «poll» más adelante, cuando ocurra algo relevante (por ejemplo, cuando termine una operación de E/S).
  • Este mecanismo de «poll» permite ejecutar muchas operaciones asíncronas de forma concurrente en un solo hilo.

Su código async es un Future

Cuando escribe una async fn, Rust la transforma en una máquina de estados que implementa el trait Future. El cálculo real solo comienza cuando se hace «poll» del Future devuelto.

Veamos una función async sencilla. No se ejecuta hasta que un ejecutor espera su resultado.

async fn say_hello() -> String {
    "Hello from a Future!".to_string()
}

#[tokio::main]
async fn main() {
    let future = say_hello(); // This doesn't run the function yet!
    println!("Future created, but not awaited.");
    // To run it, an executor needs to poll it, often via .await
    // let result = future.await;
    // println!("{}", result);
}

Los Futures se convierten en tareas

Mientras que un Future es la definición de un cálculo asíncrono, una tarea es una instancia activa de ese Future que un ejecutor impulsa hasta completarlo.

  • Cuando indica a un ejecutor (como Tokio) que ejecute un Future, este lo envuelve en una tarea.
  • A continuación, el ejecutor administra esta tarea y hace «poll» de ella cada vez que está lista para avanzar.
  • Las tareas son las unidades de trabajo que el runtime asíncrono planifica y ejecuta de forma concurrente.

Crear tareas para lograr concurrencia

Para ejecutar un Future de forma concurrente con otro código, debe «spawn»-earlo en el runtime de Tokio. Esto crea una nueva tarea que el runtime administrará.

La función tokio::spawn recibe un Future y devuelve un JoinHandle, que puede usar para esperar a que la tarea termine y obtener su resultado.

async fn perform_task(id: u8) -> String {
    tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
    format!("Task {} finished!", id)
}

#[tokio::main]
async fn main() {
    println!("Main started.");
    let handle = tokio::spawn(async {
        perform_task(1).await
    });
    println!("Task 1 spawned.");
    // We will await 'handle' in the next scene to get the result.
    // let result = handle.await.unwrap();
    // println!("{}", result);
}

Obtener resultados de las tareas

El JoinHandle devuelto por tokio::spawn es, a su vez, un Future. Puede esperar este handle con .await para esperar a que termine la tarea creada y recuperar su valor de retorno.

Si la tarea creada entra en pánico, esperar su JoinHandle devolverá un error.

async fn perform_task(id: u8) -> String {
    tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
    format!("Task {} finished!", id)
}

#[tokio::main]
async fn main() {
    println!("Main started.");
    let handle = tokio::spawn(async {
        perform_task(1).await
    });
    println!("Task 1 spawned.");
    let result = handle.await.unwrap(); // Await the handle to get the result
    println!("{}", result);
    println!("Main finished.");
}

Varias tareas concurrentes

La potencia de las tareas y de tokio::spawn se aprecia realmente al ejecutar varias operaciones de forma concurrente. El runtime de Tokio cambia eficientemente de una tarea a otra a medida que están listas.

Esto permite que su programa avance en muchas cosas a la vez sin bloquearse, incluso en un solo hilo.

async fn long_task(id: u8) -> String {
    tokio::time::sleep(tokio::time::Duration::from_millis(100 + id as u64 * 50)).await;
    format!("Long task {} done!", id)
}

#[tokio::main]
async fn main() {
    println!("Main started.");
    let handle1 = tokio::spawn(long_task(1));
    let handle2 = tokio::spawn(long_task(2));
    println!("Both tasks spawned.");
    let result1 = handle1.await.unwrap();
    let result2 = handle2.await.unwrap();
    println!("{}\n{}", result1, result2);
    println!("Main finished.");
}

Esperar a todas con join!

Cuando necesita esperar a que varios Futures se completen al mismo tiempo, tokio::join! es una macro útil. Espera de forma concurrente a todos los futures indicados y devuelve sus resultados como una tupla.

Es similar a esperar cada handle individualmente, pero suele ser más conciso cuando el número de futures es fijo.

async fn fetch_data(source: &str) -> String {
    tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
    format!("Data from {}", source)
}

#[tokio::main]
async fn main() {
    println!("Starting data fetches...");
    let (data_a, data_b) = tokio::join!(
        fetch_data("Server A"),
        fetch_data("Database B")
    );
    println!("Fetched: {}\nFetched: {}", data_a, data_b);
    println!("All fetches complete.");
}

Gestionar errores de las tareas

Las operaciones asíncronas pueden fallar, igual que las síncronas. Es habitual que los Futures devuelvan un tipo Result, que indica si la operación tuvo éxito o falló.

Al esperar un JoinHandle, recuerde que el resultado está envuelto en otro Result, porque la propia tarea podría entrar en pánico. A menudo verá handle.await? (si está dentro de una async fn que devuelve Result) o handle.await.unwrap() / .expect().

async fn might_fail(should_fail: bool) -> Result<String, &'static str> {
    tokio::time::sleep(tokio::time::Duration::from_millis(30)).await;
    if should_fail {
        Err("Oops, something went wrong!")
    } else {
        Ok("Operation successful!".to_string())
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let handle_ok = tokio::spawn(might_fail(false));
    let handle_err = tokio::spawn(might_fail(true));

    let result_ok = handle_ok.await??; // Await handle, then await inner Result
    println!("{}", result_ok);

    match handle_err.await? { // Await handle, then handle inner Result
        Ok(val) => println!("{}", val),
        Err(e) => eprintln!("Error: {}", e),
    }
    Ok(())
}

Comprender los Futures y las tareas

Ha aprendido sobre los Futures como cálculos y las tareas como sus instancias de ejecución. ¿Qué afirmación sobre tokio::spawn y JoinHandle es verdadera?

Repaso: Futures y tareas

¡Excelente trabajo! Ya comprende los conceptos fundamentales de los Futures y las tareas en el ecosistema asíncrono de Rust:

  • Un Future es un trait que representa un cálculo asíncrono que finalmente producirá un valor.
  • Las async fn se compilan en tipos que implementan el trait Future.
  • Una tarea es una instancia de un Future que un ejecutor (como Tokio) administra activamente y somete a «poll».
  • tokio::spawn se usa para crear una nueva tarea y devuelve un JoinHandle.
  • Debe hacer .await sobre un JoinHandle para obtener el resultado de una tarea creada.

¡Estos bloques de construcción son esenciales para escribir aplicaciones Rust eficientes y no bloqueantes!

Gratis para empezar

Aprende Rust con un tutor de IA — gratis

Escribe y ejecuta código real en tu navegador, obtén ayuda instantánea de un tutor de IA disponible 24/7 y continúa donde lo dejaste en la web o en la aplicación.

Cursos
39
Lecciones
144

Preguntas frecuentes

¿La lección «Trabajo con Futures y tareas» es gratis?

Sí — el texto completo de «Trabajo con Futures y tareas» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Learn Rust Coding, actualiza a CoddyKit PRO. El curso de Learn Rust Coding incluye 3 lecciones en total.

¿Qué aprenderé en «Trabajo con Futures y tareas»?

Comprenda el trait `Future` y cómo se planifican y gestionan las tareas dentro de un entorno de ejecución asíncrono. Practicas Learn Rust Coding con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Learn Rust Coding?

No se requiere experiencia previa. Learn Rust Coding en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 3.

¿Cuánto tiempo toma la lección «Trabajo con Futures y tareas»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Learn Rust Coding?

Sí. Cada lección de Learn Rust Coding incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Introducción a Async/Await
  2. Construcción de aplicaciones asíncronas con Tokio
  3. Trabajo con Futures y tareas
← Volver a Learn Rust Coding