Async / await in iterator closures

#1

Hi!
I’m struggling to call an async function from a closure on an iterator - e.g. in a call to map. Here is a minimal contrived example that fails to compile. I’d like to end up with a collection (e.g. a Vec) after awaiting each of the async calls.
Am I missing something simple to make this work?

#![feature(await_macro, async_await, futures_api)]


async fn do_something(num: i32) -> i32 {
    num + 1
}

async fn runner() {
    
    let results = (0..5).map(|i| do_something(i) )
    .map(|future| await! { future } )
    .collect::<Vec<i32>>();
    
    println!("Got {:?}", results);
}

(Playground)

Errors:

    Compiling playground v0.0.1 (/playground)
error[E0628]: generators cannot have explicit arguments
  --> src/lib.rs:11:10
   |
11 |     .map(|future| await! { future } )
   |          ^^^^^^^^

The normal way I would deal with the error is to wrap it in an async block - but then would have to explicitly poll the future manually. Can this be achieved with the await! syntax?
(Edited to make the actual issue more clear)

#2

This can’t work. You can use await only in async functions, but the closure is another different function and its not async, so you can’t await in there.

#3

Right. Thanks @kornel.
Is there an idomatic way to achieve the same goal?
i.e. I have an async function and I need to call it with each item from a collection, and collect the results.

#4

Using futures-preview you can create an iterator of futures then collect them into a FuturesOrdered and collect the results back into a vector, something like

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

async fn runner() {
    let results = await!((0..5).map(|i| do_something(i)).collect::<FuturesOrdered<_>>().collect::<Vec<_>>());
    println!("Got {:?}", results);
}

or use join_all to run them all to completion

use futures::future;

async fn runner() {
    let results = await!(future::join_all((0..5).map(|i| do_something(i))));
    println!("Got {:?}", results);
}

Depending on what sort of async operation you’re doing and how many elements there are one or the other will be more efficient, but they should both work.

1 Like
#5

Ah thanks @Nemo157! That is exactly what I was looking for.
This also indirectly helped me discover the rename dependencies feature in cargo, as I had a bit of a muddle trying to use futures 0.1 (for some tokio stuff) and futures-preview together.
All solved, thanks.