Search code examples
asynchronousrustasync-awaitcommand-line-interfacerust-tokio

Execute a long running task in background and show progress until it's not complete in Rust


I have a CLI-based application that performs long computations in the background. While it performs the background computation, I want to display progress in the console, and once the background task is complete I want to use the return code to display the correct message ex: update success/ failed, etc.

Here's an example of how the application will look like:

Updating the driver

................................

Update completed successfully

To execute the task async fn update_device() -> u8{ } in the background I can use async function and call tokio::task::spawn() on it, and call .await on the JoinHandle. But I want to know how can I show the progress, while the updates are going.

I checked the JoinHandle in tokio has a function is_finished(), but I don't know how to call it or use it for this purpose.

I want to display progress in the console, and once the background task is complete I want to use the return code to display the correct message ex: update success/ failed, etc.

When I try to call is_finished before awaiting like this:

async fn update_device() -> u8{
    tokio::time::sleep(Duration::from_sec(5000)).await;
}

fn test(){

    println!("Updating the driver");
    let handle = tokio::task::spawn(update_device());

    loop{
        if handle.is_finished() == true{
            break;
        }
        print!(".")
    }

    println!("Driver Update succesfully");
}


fn main() {
    test()
}

I get the error

there's no reactor running, must be called from the context of a Tokio 1.x runtime

Solution

  • is_finished has to be called from within a running runtime, otherwise there is nothing that could advance the Future and thus it would always return the same false, the easiest to adapt your code is to use the attribute #[tokio::main]:

    use std::io::Write;
    use core::time::Duration;
    #[tokio::main]
    async fn test() {
        let h = tokio::task::spawn(update_device());
        while !h.is_finished() {
            tokio::time::sleep(Duration::from_millis(250)).await;
            print!(".");
            std::io::stdout().flush().unwrap();
        }
        println!();
        if h.await.unwrap() == 0 {
            println!("Update completed successfully");
        } else {
            println!("Update failed");
        }
    
    }
    
    async fn update_device() -> u8 {
        println!("Updating the driver");
        tokio::time::sleep(Duration::from_secs(2)).await;
        0
    }
    
    fn main() {
        test()
    }
    

    Playground