4 ms·
> Does this mean then that one, two, three all have their execution scheduled synchronously with no way to express parallelism? > Or is the compiler figuring o
by beltsazar 5y ago
> Does this mean then that one, two, three all have their execution scheduled synchronously with no way to express parallelism?
> Or is the compiler figuring out the dependencies and generating a magic state machine where non-dependencies are spawned in parallel and joined before returning?
No for both.
Firstly, when you do:
let one = my_fn_one();
the variable `one` is not of type u32 (no uint32 in Rust), but of type* Future<Output=u32>. Therefore, you can't actually pass it into my_fn_three, which takes u32 as a parameter. Also, when you do `my_fn_one()`, you don't actually call the function—the body of my_fn_one is executed only when it's awaited, i.e. `my_fn_one().await` or:
let one_future = my_fn_one();
let one_value: u32 = one_future.await;
Back to your original questions. When an async function foo awaits another async function bar, from the foo's perspective the awaiting operation is "blocking", which means the next lines of code won't be executed until it is finished. For example:
async fn foo() {
let x = bar().await;
let y = 42; // This line won't be executed until awaiting bar is finished
let z = baz().await; // Neither will this line
}
So, the answer to your first question is one, two, three all will be executed "synchronously" / "sequentially".
To your second question, the compiler won't create a dependency graph of futures and concurrently execute futures that are independent to each other. The good news is that futures can be executed concurrently, but you need to explicitly build a dependency graph, by spawning a task [1] and/or using `futures::join` macro [2].
For example, you can concurrently execute my_fn_two and my_fn_three by:
async fn mixup() {
let one_value = my_fn_one().await;
// Execute my_fn_two and my_fn_three concurrently.
// Notice that there is no await because we pass futures to futures::join.
futures::join!(my_fn_two(), my_fn_three(one_value));
}
Or better, because my_fn_two is independent from my_fn_one, you can concurrently execute {my_fn_one, my_fn_three} and {my_fn_two} by:
async fn mixup() {
let one_three_future = async {
let one_value = my_fn_one().await;
my_fn_three(one_value).await;
};
futures::join!(my_fn_two(), one_three_future);
}
[1] https://docs.rs/tokio/1.7.0/tokio/task/fn.spawn.html https://docs.rs/tokio/1.7.0/tokio/task/fn.spawn.html
[2] https://docs.rs/futures/0.3.15/futures/macro.join.html https://docs.rs/futures/0.3.15/futures/macro.join.html
* or more precisely "of an anonymous type that implements"
- vlovich123 5y agoThis is exactly what I'm raising. You describe the system as today. I'm talking about the article's proposal of implicit await. Today, async functions return a Future<T>. With implicit await described in the article, they return a T. So, in an implicit await world, how do I express the mixup function?