async fn query(page: i32) -> String{
let client = reqwest::Client::new();
let url = "http://abc.com";
let body = client.get(&url)
.header("DEVICE-TYPE", "android")
.send().await.unwrap().text().await.unwrap();
dbg!(&body);
body
}
#[tokio::main]
async fn main() {
for x in 0..10 {
let partial_result = thread::spawn(move||{
let body = block_on(query(y));
});
}
loop {}
}
cargo.toml
[dependencies]
reqwest = { version = "0.11", features = ["blocking", "json"] }
tokio = { version = "0.2", features = ["full"] }
serde_json = "1.0"
futures = "0.3.30"
async-std = { version = "1", features = ["attributes", "tokio1"] }
error:
there is no reactor running, must be called from the context of a Tokio 1.x runtime
reqwest must be executed from a tokio runtime. You don't execute query(y) in a tokio runtime but spawn a new thread which then uses some other executor (I assume futures') to poll the future.
You'd do it solely in the tokio runtime. #[tokio::main] already creates a multithreaded runtime for you, so you have a bunch of worker threads (defaults to the amount of (virtual?) cores on your system) already at your disposal. No need to spawn additional threads not managed by tokio. All you need to do is spawn tasks onto the worker threads and let them do the fetching. Here an example using JoinSet to make your requests execute in parallel:
#[tokio::main(worker_threads = 10)]
async fn main() {
let mut set = JoinSet::new();
for x in 0..10 {
set.spawn(async move { query(x).await });
}
while let Some(q) = set.join_next().await {
let res = q.unwrap();
println!("{res}");
}
}
join_all is ordered, JoinSet isn't. Be sure that you spawn you query invocation onto the worker threads with tokio::task::spawn, rather than putting them in your future_vec directly, or else they will all run concurrently on the main thread and not in parallel on the worker threads.
Spawning is usually more efficient than join_all. With spawning, the runtime can manage each individual task separately, whereas it has less control over them with join_all.