如何在不等待的情况下发送请求?
How do I send request without await?
我正在使用 reqwest,并尝试每 97 毫秒发送一次请求。但是,我不想等待最后一个请求发生或被读取。
我只想请求每 97 毫秒发送一次,并始终将输出发送到标准输出。
我的(当前)代码是这样的:(keys
是一个带有 api 个键的数组)
loop {
for i in keys.iter() {
let url = format!("https://api.[insertsite].com/blahblah&apiKey={}", i);
let res = client
.get(url)
.send()
.await?
.text()
.await?;
sleep(Duration::from_millis(97)).await;
println!("{:?}", res);
}
}
如果我删除等待编译器告诉我 error[E0599]: no method named `text` found for opaque type `impl std::future::Future` in the current scope
.
TL;DR:我想每 97 毫秒发送一次获取请求,无论是否有任何响应。 If/when 有一个响应 stdout 的响应管道。
编辑:
我试过使用线程,但我真的不知道该怎么做。这是我想出的:
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let keys = ["key", "key", "key"];
let client = reqwest::Client::builder()
.build()?;
for i in keys.iter() {
tokio::spawn(async move {
let url = format!("https://api[insertsite].com/blahblah&apiKey={}", i);
let res = client
.get(url)
.send()
.await
.text()
.await;
println!("{:?}", res);
});
sleep(Duration::from_millis(97)).await;
}
}
我在尝试 运行:
时收到此错误
error[E0599]: no method named `text` found for enum `std::result::Result<reqwest::Response, reqwest::Error>` in the current scope
--> src/main.rs:22:18
|
22 | .text()
| ^^^^ method not found in `std::result::Result<reqwest::Response, reqwest::Error>`
您已经发现 tokio::spawn
是完成此任务的正确工具,因为您本质上想以“即发即弃”的方式使用一些异步代码,而不是在主程序中等待它程序流程。您只需要对代码进行一些调整。
首先,对于您引用的错误 - 这是因为您必须以某种方式处理请求期间可能出现的错误。您可以在每个 await
returning Result
之后简单地添加问号,但随后您 运行 进入以下内容:
error[E0277]: the `?` operator can only be used in an async block that returns `Result` or `Option` (or another type that implements `Try`)
--> src/main.rs:11:23
|
9 | tokio::spawn(async move {
| _________________________________-
10 | | let url = format!("https://api[insertsite].com/blahblah&apiKey={}", i);
11 | | let res = client.get(url).send().await?.text().await?;
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in an async block that returns `()`
12 | | println!("{:?}", res);
13 | | });
| |_________- this function should return `Result` or `Option` to accept `?`
|
= help: the trait `Try` is not implemented for `()`
= note: required by `from_error`
“这个函数”位可能有点误导,因为错误的其余部分是在谈论“异步块”,但要点很简单:如果你使用问号从某个地方冒出错误,快乐之路也必须returnResult
。好吧,让我们简单地将 Ok(())
添加到异步块的末尾,这是下一个错误:
error[E0282]: type annotations needed
--> src/main.rs:11:51
|
11 | let res = client.get(url).send().await?.text().await?;
| ^ cannot infer type of error for `?` operator
|
= note: `?` implicitly converts the error value into a type implementing `From<reqwest::Error>`
这也是预期的 - 在普通函数中 return 类型将由其签名提供,但在异步块中这不是一个选项。但是,我们可以使用涡轮鱼:
tokio::spawn(async move {
// snip
Ok::<_, Box<dyn std::error::Error + Send + Sync>>(())
});
请注意,我们需要 Send
和 Sync
:前者是生成的错误能够跨越异步块边界所必需的,后者是因为 Box<dyn Error + Send>
can't be created with From/Into
conversion from other errors.
现在,我们还剩下两个独立于之前的编译错误:
error[E0597]: `keys` does not live long enough
--> src/main.rs:8:14
|
8 | for i in keys.iter() {
| ^^^^
| |
| borrowed value does not live long enough
| cast requires that `keys` is borrowed for `'static`
...
18 | }
| - `keys` dropped here while still borrowed
error[E0382]: use of moved value: `client`
--> src/main.rs:9:33
|
7 | let client = reqwest::Client::builder().build()?;
| ------ move occurs because `client` has type `reqwest::Client`, which does not implement the `Copy` trait
8 | for i in keys.iter() {
9 | tokio::spawn(async move {
| _________________________________^
10 | | let url = format!("https://api[insertsite].com/blahblah&apiKey={}", i);
11 | | let res = client.get(url).send().await?.text().await?;
| | ------ use occurs due to use in generator
12 | | println!("{:?}", res);
13 | | Ok::<_, Box<dyn std::error::Error + Send + Sync>>(())
14 | | });
| |_________^ value moved here, in previous iteration of loop
第一个问题可以通过三种可能的方式修复:
- 使用
Vec
代替数组并删除 .iter()
,
- 使用
std::array::IntoIter
,
- 使用
.iter().copied()
。
在每种情况下,我们都会得到 &'static str
,可以将其传递到异步块中。
第二个更简单:我们可以简单地在每次迭代开始时 tokio::spawn
之前执行 let client = client.clone();
。这很便宜,因为 Client
uses Arc
internally.
这是 playground,包含上述所有更改。
最后,这是我个人建议您的代码基于的版本,因为它不仅可以编译,而且不会吞没根据要求可能出现的错误:
use core::time::Duration;
use tokio::time::sleep;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let keys = ["key", "key", "key"];
let client = reqwest::Client::builder().build()?;
for i in std::array::IntoIter::new(keys) {
let client = client.clone();
tokio::spawn(async move {
let url = format!("https://api[insertsite].com/blahblah&apiKey={}", i);
// moved actual request into inner block...
match async move {
let res = client.get(url).send().await?.text().await?;
// ...returning Result with explicit error type,
// so that the wholeinner async block is treated as "try"-block...
Ok::<_, Box<dyn std::error::Error + Send + Sync>>(res)
}
.await
{
// ...and matching on the result, to have either text or error
// (here it'll always be error, due to invalid URL)
Ok(res) => println!("{:?}", res),
Err(er) => println!("{}", er),
}
});
sleep(Duration::from_millis(97)).await;
}
// just to wait for responses
sleep(Duration::from_millis(1000)).await;
Ok(())
}
我正在使用 reqwest,并尝试每 97 毫秒发送一次请求。但是,我不想等待最后一个请求发生或被读取。
我只想请求每 97 毫秒发送一次,并始终将输出发送到标准输出。
我的(当前)代码是这样的:(keys
是一个带有 api 个键的数组)
loop {
for i in keys.iter() {
let url = format!("https://api.[insertsite].com/blahblah&apiKey={}", i);
let res = client
.get(url)
.send()
.await?
.text()
.await?;
sleep(Duration::from_millis(97)).await;
println!("{:?}", res);
}
}
如果我删除等待编译器告诉我 error[E0599]: no method named `text` found for opaque type `impl std::future::Future` in the current scope
.
TL;DR:我想每 97 毫秒发送一次获取请求,无论是否有任何响应。 If/when 有一个响应 stdout 的响应管道。
编辑:
我试过使用线程,但我真的不知道该怎么做。这是我想出的:
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let keys = ["key", "key", "key"];
let client = reqwest::Client::builder()
.build()?;
for i in keys.iter() {
tokio::spawn(async move {
let url = format!("https://api[insertsite].com/blahblah&apiKey={}", i);
let res = client
.get(url)
.send()
.await
.text()
.await;
println!("{:?}", res);
});
sleep(Duration::from_millis(97)).await;
}
}
我在尝试 运行:
时收到此错误error[E0599]: no method named `text` found for enum `std::result::Result<reqwest::Response, reqwest::Error>` in the current scope
--> src/main.rs:22:18
|
22 | .text()
| ^^^^ method not found in `std::result::Result<reqwest::Response, reqwest::Error>`
您已经发现 tokio::spawn
是完成此任务的正确工具,因为您本质上想以“即发即弃”的方式使用一些异步代码,而不是在主程序中等待它程序流程。您只需要对代码进行一些调整。
首先,对于您引用的错误 - 这是因为您必须以某种方式处理请求期间可能出现的错误。您可以在每个 await
returning Result
之后简单地添加问号,但随后您 运行 进入以下内容:
error[E0277]: the `?` operator can only be used in an async block that returns `Result` or `Option` (or another type that implements `Try`)
--> src/main.rs:11:23
|
9 | tokio::spawn(async move {
| _________________________________-
10 | | let url = format!("https://api[insertsite].com/blahblah&apiKey={}", i);
11 | | let res = client.get(url).send().await?.text().await?;
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in an async block that returns `()`
12 | | println!("{:?}", res);
13 | | });
| |_________- this function should return `Result` or `Option` to accept `?`
|
= help: the trait `Try` is not implemented for `()`
= note: required by `from_error`
“这个函数”位可能有点误导,因为错误的其余部分是在谈论“异步块”,但要点很简单:如果你使用问号从某个地方冒出错误,快乐之路也必须returnResult
。好吧,让我们简单地将 Ok(())
添加到异步块的末尾,这是下一个错误:
error[E0282]: type annotations needed
--> src/main.rs:11:51
|
11 | let res = client.get(url).send().await?.text().await?;
| ^ cannot infer type of error for `?` operator
|
= note: `?` implicitly converts the error value into a type implementing `From<reqwest::Error>`
这也是预期的 - 在普通函数中 return 类型将由其签名提供,但在异步块中这不是一个选项。但是,我们可以使用涡轮鱼:
tokio::spawn(async move {
// snip
Ok::<_, Box<dyn std::error::Error + Send + Sync>>(())
});
请注意,我们需要 Send
和 Sync
:前者是生成的错误能够跨越异步块边界所必需的,后者是因为 Box<dyn Error + Send>
can't be created with From/Into
conversion from other errors.
现在,我们还剩下两个独立于之前的编译错误:
error[E0597]: `keys` does not live long enough
--> src/main.rs:8:14
|
8 | for i in keys.iter() {
| ^^^^
| |
| borrowed value does not live long enough
| cast requires that `keys` is borrowed for `'static`
...
18 | }
| - `keys` dropped here while still borrowed
error[E0382]: use of moved value: `client`
--> src/main.rs:9:33
|
7 | let client = reqwest::Client::builder().build()?;
| ------ move occurs because `client` has type `reqwest::Client`, which does not implement the `Copy` trait
8 | for i in keys.iter() {
9 | tokio::spawn(async move {
| _________________________________^
10 | | let url = format!("https://api[insertsite].com/blahblah&apiKey={}", i);
11 | | let res = client.get(url).send().await?.text().await?;
| | ------ use occurs due to use in generator
12 | | println!("{:?}", res);
13 | | Ok::<_, Box<dyn std::error::Error + Send + Sync>>(())
14 | | });
| |_________^ value moved here, in previous iteration of loop
第一个问题可以通过三种可能的方式修复:
- 使用
Vec
代替数组并删除.iter()
, - 使用
std::array::IntoIter
, - 使用
.iter().copied()
。 在每种情况下,我们都会得到&'static str
,可以将其传递到异步块中。
第二个更简单:我们可以简单地在每次迭代开始时 tokio::spawn
之前执行 let client = client.clone();
。这很便宜,因为 Client
uses Arc
internally.
这是 playground,包含上述所有更改。
最后,这是我个人建议您的代码基于的版本,因为它不仅可以编译,而且不会吞没根据要求可能出现的错误:
use core::time::Duration;
use tokio::time::sleep;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let keys = ["key", "key", "key"];
let client = reqwest::Client::builder().build()?;
for i in std::array::IntoIter::new(keys) {
let client = client.clone();
tokio::spawn(async move {
let url = format!("https://api[insertsite].com/blahblah&apiKey={}", i);
// moved actual request into inner block...
match async move {
let res = client.get(url).send().await?.text().await?;
// ...returning Result with explicit error type,
// so that the wholeinner async block is treated as "try"-block...
Ok::<_, Box<dyn std::error::Error + Send + Sync>>(res)
}
.await
{
// ...and matching on the result, to have either text or error
// (here it'll always be error, due to invalid URL)
Ok(res) => println!("{:?}", res),
Err(er) => println!("{}", er),
}
});
sleep(Duration::from_millis(97)).await;
}
// just to wait for responses
sleep(Duration::from_millis(1000)).await;
Ok(())
}