Async Rust is powerful. And it can be a pain to work with (and learn). Async Rust can be a pleasure to work with, though, if we can do it without `Send + Sync + 'static`.
The most interesting part here is the polling only has to take place on the scope itself. That was actually what I wanted to check, but got distracted because all spawns are awaited in the scope in moro’s README example.
f1 and f2 are run concurrently, f3 is run after f2 finishes, but doesn’t have to wait for f1 to finish, which is maybe obvious, but… (see below).
So two things here:
Re-using the spawn terminology here irks me for some reason. I don’t know what would be better though. Would defer_to_scope() be confusing if the job is awaited in the scope?
Even if assumed obvious, a note about execution order when there is a mix of awaited and non-awaited jobs is worth adding to the documentation IMHO.
The most interesting part here is the polling only has to take place on the scope itself. That was actually what I wanted to check, but got distracted because all spawns are awaited in the scope in
moro
’s README example.async fn slp() { tokio::time::sleep(std::time::Duration::from_millis(1)).await } async fn _main() { let result_fut = moro::async_scope!(|scope| { dbg!("d1"); scope.spawn(async { dbg!("f1a"); slp().await; slp().await; slp().await; dbg!("f1b"); }); dbg!("d2"); // 11 scope.spawn(async { dbg!("f2a"); slp().await; slp().await; dbg!("f2b"); }); dbg!("d3"); // 14 scope.spawn(async { dbg!("f3a"); slp().await; dbg!("f3b"); }); dbg!("d4"); async { dbg!("b1"); } // never executes }); slp().await; dbg!("o1"); let _ = result_fut.await; } fn main() { let rt = tokio::runtime::Builder::new_multi_thread() .enable_all() .build() .unwrap(); rt.block_on(_main()) }
[src/main.rs:32:5] "o1" = "o1" [src/main.rs:7:9] "d1" = "d1" [src/main.rs:15:9] "d2" = "d2" [src/main.rs:22:9] "d3" = "d3" [src/main.rs:28:9] "d4" = "d4" [src/main.rs:9:13] "f1a" = "f1a" [src/main.rs:17:13] "f2a" = "f2a" [src/main.rs:24:13] "f3a" = "f3a" [src/main.rs:26:13] "f3b" = "f3b" [src/main.rs:20:13] "f2b" = "f2b" [src/main.rs:13:13] "f1b" = "f1b"
The non-awaited jobs are run concurrently as the moro docs say. But what if we immediately await f2?
[src/main.rs:32:5] "o1" = "o1" [src/main.rs:7:9] "d1" = "d1" [src/main.rs:15:9] "d2" = "d2" [src/main.rs:9:13] "f1a" = "f1a" [src/main.rs:17:13] "f2a" = "f2a" [src/main.rs:20:13] "f2b" = "f2b" [src/main.rs:22:9] "d3" = "d3" [src/main.rs:28:9] "d4" = "d4" [src/main.rs:24:13] "f3a" = "f3a" [src/main.rs:13:13] "f1b" = "f1b" [src/main.rs:26:13] "f3b" = "f3b"
f1 and f2 are run concurrently, f3 is run after f2 finishes, but doesn’t have to wait for f1 to finish, which is maybe obvious, but… (see below).
So two things here:
defer_to_scope()
be confusing if the job is awaited in the scope?