In a lot of SO questions and also in a lot of external, reputable resources, such as the tokio tutorial I've seen people use an additional async
block such as:
tokio::spawn(async move {
process(socket).await;
});
instead of doing just
tokio::spawn(process(socket));
Both resolve to some Future
and spawn()
expects
pub fn spawn<T>(future: T) ....
where T: Future + ....
so I cannot understand the need for the additional async {}
block.
Is it really necessary ? And is there any difference besides being a "visual marking" that this is an async code?
Note: The purpose of async / await is to simplify the syntax necessary to consume promise-based APIs. The behavior of async / await is similar to combining generators and promises. Async functions always return a promise.
An async keyword is a method that performs asynchronous tasks such as fetching data from a database, reading a file, etc, they can be marked as “async”. Whereas await keyword making “await” to a statement means suspending the execution of the async method it is residing in until the asynchronous task completes.
Benefits of Asynchronous ProgrammingImprove your application's performance and responsiveness, especially if you have long-running operations that don't require blocking the execution. In this case, you can do other things while you wait for the long-running Task to finish.
The async keyword turns a method into an async method, which allows you to use the await keyword in its body. When the await keyword is applied, it suspends the calling method and yields control back to its caller until the awaited task is complete.
These are slightly different, depending on the implementation of process
.
If process
is defined like this:
fn process(socket: Socket) -> impl Future {
// non-async stuff
println!("non-async stuff");
// async stuff
async {
println!("async stuff");
}
}
Then invoking it with tokio::spawn(process(socket));
will execute the first line immediately and only the remaining sub-task, which prints "async stuff"
, will be scheduled.
If it defined like this:
async fn process(socket: Socket) {
// non-async stuff
println!("non-async stuff");
// async stuff
async {
println!("async stuff");
}.await;
}
Then tokio::spawn(process(socket));
will initally do nothing and will print both "non-async stuff"
and "async stuff"
when the scheduled task is executed.
Wrapping the call in a extra async
block will make the two versions behave the same way. Whether or not this is the reason for the extra async
block or if it is "needed" in the examples that you've seen is a question for the authors of those examples.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With