#Best way to execute an iterable of futures concurrently?

11 messages · Page 1 of 1 (latest)

abstract willow
#

I have a Vec<String> and would like to map each element of this Vec to an async function. The async function doesn't return anything, so I do not care about the output. What is the fastest way to execute these async function calls? If I use a naive for-loop, I believe they execute in series, which is not desired. I found futures::future::join_all and tokio::spawn but am not sure if this is the way I should be approaching the problem. Here is what I have so far:

use futures::future::join_all;

async fn do_something_async(param: String) -> () {
    ...
}

#[tokio::main]
async fn main() {
    let foo: Vec<String> ... ;
    join_all(
        foo.iter().map(|el| {do_something_async(el)})
    ).await;
}
novel galleon
#

?eval

use futures::{stream::FuturesUnordered, StreamExt};

#[tokio::main]
async fn main() {
    let foo = vec![300, 100, 200];

    foo.into_iter()
        .map(process)
        .collect::<FuturesUnordered<_>>()
        .collect::<()>()
        .await;
}

async fn process(x: u64) {
    tokio::time::sleep(std::time::Duration::from_millis(x)).await;
    println!("processed {x}");
}
tulip shoreBOT
#
     Running `target/debug/playground`

processed 100
processed 200
processed 300
abstract willow
#

Why does FuturesUnordered need to be collected into () before awaiting?

novel galleon
#

it represents a stream. One way to consume a stream can be collecting it

abstract willow
#

Oh, I see

#

I'll try this out - thanks!

novel galleon
#

tokio::spawning each task could also enable parallelism in addition to concurrency if you have a multi-threaded runtime

#

but if your async processing isn't really CPU bound then it might not be worth the trouble