Async with tokio

Regular functions block threads

Async doesn’t block threads

Before we look at what tokio does, let’s consider how we’d run 3 tasks if they were ‘blocking’

Without Tokio (blocking):

If you used std::thread::sleep, the execution would pause (block) for each function call until the sleep duration completes. This means that when you execute the code sequentially, the first do_stuff would block the execution for its sleep time, followed by the second, and then the third. The tasks would not run concurrently.

With Tokio (non-blocking):

Using tokio::time::sleep with async/await, the program can yield control while sleeping. This allows other tasks to proceed during the sleep period without blocking the thread. When you use join!, all three do_stuff functions can run concurrently without blocking the thread, making them execute in parallel (asynchronously).

In summary, with Tokio, the code becomes asynchronous, allowing concurrent execution, while without Tokio or an asynchronous runtime, the execution would be blocking and sequential.

non-blocking with toki

Make the thread sleep WITHOUT tokio::time::sleep

use std::thread;
use tokio::*; 
use rand::{thread_rng, Rng};


async fn do_stuff(nm:i32){
    let rn = thread_rng().gen_range(1..100);
    thread::sleep(std::time::Duration::from_millis(rn));
    //tokio::time::sleep(std::time::Duration::from_millis(rn)).await;
    println!("{:?}",nm );
}

#[tokio::main]
async fn main() {
    join!(do_stuff(1), do_stuff(2), do_stuff(3));
}
1
2
3

[Process exited 0]
//use std::thread;
use tokio::*; 
use rand::{thread_rng, Rng};


async fn do_stuff(nm:i32){
    let rn = thread_rng().gen_range(1..100);
    //thread::sleep(std::time::Duration::from_millis(rn));
    tokio::time::sleep(std::time::Duration::from_millis(rn)).await;
    println!("{:?}",nm );
}

#[tokio::main]
async fn main() {
    join!(do_stuff(1), do_stuff(2), do_stuff(3));
}

3
2
1

[Process exited 0]

try_join

use tokio::*; 
use rand::{thread_rng,Rng};


async fn do_stuff(nm:i32)-> Result<(), Box<dyn std::error::Error>>{
    let rn = thread_rng().gen_range(1..100);
    tokio::time::sleep(std::time::Duration::from_millis(rn)).await;
    println!("{:?}",nm );
        // Simulate a failure for nm == 2
    if nm % 2 ==0 {
        return Err(format!("Future {} failed!", nm).into());
    }
    Ok(())
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let res = tokio::try_join!(do_stuff(1), do_stuff(2), do_stuff(3));
    match res {
        Ok(_)=>{println!("{:?}", "All futures suceeded")},
        Err(e)=>{println!("Errrr{}",e)}
    }
    Ok(())
}