3

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.

Oskar Persson
  • 6,605
  • 15
  • 63
  • 124
  • You could ideally write an `Actor` and use the do_send method. https://docs.rs/actix/0.10.0-alpha.3/actix/struct.Addr.html#method.do_send I could write you an answer if that is what you are looking for. – Njuguna Mureithi Aug 17 '20 at 16:44

1 Answers1

2

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

Aplet123
  • 33,825
  • 1
  • 29
  • 55
  • I don't think this does what I'm after, if I'm understanding your code correctly. I've updated my question with some more details and a some what more proper example of what I'm after. – Oskar Persson Jul 19 '20 at 20:01
  • @OskarPersson I think it does point the way though. You can spawn a tokio task in your index function to perform the heavy computation. – Brendan Wilson Jul 19 '20 at 20:27
  • This helped me achieve with Rocket what the OP originally wanted with Actix. The spawned `async move` block continues work after the response has been flushed to the client. – marekful Mar 27 '23 at 14:35