From 70db7fb2ea557a3e1e4da142aa193fefee55cccd Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Wed, 15 Jan 2020 16:12:44 -0800 Subject: [PATCH] Add some general documentation on async as it pertains to Rocket. --- site/guide/3-overview.md | 58 ++++++++++++++++++++++++++++++++++++++++ site/guide/4-requests.md | 24 +++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/site/guide/3-overview.md b/site/guide/3-overview.md index 677759aa..e904d456 100644 --- a/site/guide/3-overview.md +++ b/site/guide/3-overview.md @@ -209,3 +209,61 @@ A version of this example's complete crate, ready to `cargo run`, can be found on [GitHub](@example/hello_world). You can find dozens of other complete examples, spanning all of Rocket's features, in the [GitHub examples directory](@example/). + +## Futures and Async + +Rocket uses Rust `Future`s for concurrency. Asynchronous programming with +`Future`s and `async/await` allows route handlers to perform wait-heavy I/O such +as filesystem and network access while still allowing other requests to be +processed. For an overview of Rust `Future`s, see [Asynchronous Programming in +Rust](https://rust-lang.github.io/async-book/). + +In general, you should prefer to use async-ready libraries instead of +synchronous equivalents inside Rocket applications. + +`async` appears in several places in Rocket: + +* [Routes](../requests) and [Error Catchers](../requests#error-catchers) can be + `async fn`s. Inside an `async fn`, you can `.await` `Future`s from Rocket or + other libraries +* Several of Rocket's traits, such as [`FromData`](../requests#body-data) and + [`FromRequestAsync`](../requests#request-guards), have methods that return + `Future`s. +* `Data` and `DataStream` (incoming request data) and `Response` and `Body` + (outgoing response data) are based on `tokio::io::AsyncRead` instead of + `std::io::Read`. + +You can find async-ready libraries on [crates.io](https://crates.io) with the +`async` tag. + +! note + + Rocket 0.5 uses the tokio (0.2) runtime. `Rocket::launch()` will automatically + start a runtime for you, or you can create a runtime yourself and use + `Rocket::spawn()`. + +### Cooperative Multitasking + +Rust's `Future`s are a form of *cooperative multitasking*. In general, `Future`s +and `async fn`s should only `.await` on other operations and never block. Some +common examples of blocking include locking mutexes, joining threads, or using +non-`async` library functions (including those in `std`) that perform I/O. + +If a `Future` or `async fn` blocks the thread, inefficient resource usage, +stalls, or sometimes even deadlocks can occur. + +! note + + Sometimes there is no good async alternative for a library or operation. If + necessary, you can convert a synchronous operation to an async one with + `tokio::task::spawn_blocking`: + + ```rust + #[get("/blocking_task")] + async fn blocking_task() -> String { + // In a real application, we would use rocket::response::NamedFile + tokio::task::spawn_blocking(|| { + std::fs::read_file("data.txt") + }).await + } + ``` diff --git a/site/guide/4-requests.md b/site/guide/4-requests.md index ab259e2d..adf15378 100644 --- a/site/guide/4-requests.md +++ b/site/guide/4-requests.md @@ -1079,6 +1079,30 @@ returned. The handler above is complete. It really is that simple! See the [`take()`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.take +## Async Routes + +Rocket makes it easy to use `async/await` in routes. + +```rust +#[get("/weather")] +async fn weather() -> String { + let response = reqwest::get("https://www.example.com").await; + response.text().await +} +``` + +First, notice that the route function is an `async fn`. This enables +the use of `await` inside the handler. `reqwest` is an asynchronous +HTTP client, so we must `await` the response. Finally, we call +the `text()` function, which asynchronously downloads the full +response data from the server. + +! warning: You should _always_ set limits when reading incoming data. + + Just like with client input, you should usually limit the amount + of data read from external APIs. The exact method will depend + on the library you are using to make requests. + ## Error Catchers Routing may fail for a variety of reasons. These include: