Oskar Persson
Oskar Persson

Reputation: 6753

Run long running async function in background after returning response

In one of my actix-web handlers I want to call a function that run in the background and immediately return a response to the user:

async fn heavy_computation() -> {
    // do some long running computation
}

async fn index(req: HttpRequest) -> impl Responder {
    // start computation
    heavy_computation(); 
    
    // render and return template
    let out context = Context::new();
    context.insert("foo", "bar");
    render_template("my_template.html", &context)

    // computation finishes
}

#[actix_rt::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .service(web::resource("/").route(web::get().to(index)))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

If I await the future the response isn't done until after the computation and if I don't await it, the function isn't executed at all.

Upvotes: 4

Views: 3756

Answers (1)

Aplet123
Aplet123

Reputation: 35512

Assuming that you're using tokio as your async runtime, you can spawn two tasks using tokio::task::spawn then join them with tokio::join:

use tokio::task;
use tokio::time;
use std::time::Duration;

async fn heavy_computation() {
    time::delay_for(Duration::from_millis(2000)).await;
    println!("heavy computation finished");
}

async fn light_computation() {
    time::delay_for(Duration::from_millis(500)).await;
    println!("light computation finished");
}

#[tokio::main]
async fn main() {
    let heavy = task::spawn(heavy_computation());
    println!("computation started");
    let light = task::spawn(async move {
        for _ in 0..3 {
            light_computation().await;
        }
    });
    let (a, b) = tokio::join!(heavy, light);
    // use the results so the compiler doesn't complain
    a.unwrap();
    b.unwrap();
}

Link to playground

Upvotes: 3

Related Questions