rust - 如何在稳定的 Rust 中同步返回在异步 Future 中计算的值?
问题描述
我正在尝试使用 hyper 来获取 HTML 页面的内容,并希望同步返回未来的输出。我意识到我可以选择一个更好的例子,因为同步 HTTP 请求已经存在,但我更感兴趣的是了解我们是否可以从异步计算中返回一个值。
extern crate futures;
extern crate hyper;
extern crate hyper_tls;
extern crate tokio;
use futures::{future, Future, Stream};
use hyper::Client;
use hyper::Uri;
use hyper_tls::HttpsConnector;
use std::str;
fn scrap() -> Result<String, String> {
let scraped_content = future::lazy(|| {
let https = HttpsConnector::new(4).unwrap();
let client = Client::builder().build::<_, hyper::Body>(https);
client
.get("https://hyper.rs".parse::<Uri>().unwrap())
.and_then(|res| {
res.into_body().concat2().and_then(|body| {
let s_body: String = str::from_utf8(&body).unwrap().to_string();
futures::future::ok(s_body)
})
}).map_err(|err| format!("Error scraping web page: {:?}", &err))
});
scraped_content.wait()
}
fn read() {
let scraped_content = future::lazy(|| {
let https = HttpsConnector::new(4).unwrap();
let client = Client::builder().build::<_, hyper::Body>(https);
client
.get("https://hyper.rs".parse::<Uri>().unwrap())
.and_then(|res| {
res.into_body().concat2().and_then(|body| {
let s_body: String = str::from_utf8(&body).unwrap().to_string();
println!("Reading body: {}", s_body);
Ok(())
})
}).map_err(|err| {
println!("Error reading webpage: {:?}", &err);
})
});
tokio::run(scraped_content);
}
fn main() {
read();
let content = scrap();
println!("Content = {:?}", &content);
}
该示例编译并调用read()
成功,但调用scrap()
恐慌并显示以下错误消息:
Content = Err("Error scraping web page: Error { kind: Execute, cause: None }")
我知道在调用.wait()
未来之前我未能正确启动任务,但我找不到如何正确执行它,假设它甚至是可能的。
解决方案
标准库期货
让我们用它作为我们最小的、可重现的例子:
async fn example() -> i32 {
42
}
use futures::executor; // 0.3.1
fn main() {
let v = executor::block_on(example());
println!("{}", v);
}
东京
使用tokio::main
任何函数的属性(不仅仅是main
!)将其从异步函数转换为同步函数:
use tokio; // 0.3.5
#[tokio::main]
async fn main() {
let v = example().await;
println!("{}", v);
}
tokio::main
是一个转换这个的宏
#[tokio::main]
async fn main() {}
进入这个:
fn main() {
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(async { {} })
}
这Runtime::block_on
在引擎盖下使用,因此您也可以将其写为:
use tokio::runtime::Runtime; // 0.3.5
fn main() {
let v = Runtime::new().unwrap().block_on(example());
println!("{}", v);
}
对于测试,您可以使用tokio::test
.
异步标准
使用函数async_std::main
上的属性main
将其从异步函数转换为同步函数:
use async_std; // 1.6.5, features = ["attributes"]
#[async_std::main]
async fn main() {
let v = example().await;
println!("{}", v);
}
对于测试,您可以使用async_std::test
.
期货 0.1
让我们用它作为我们最小的、可重现的例子:
use futures::{future, Future}; // 0.1.27
fn example() -> impl Future<Item = i32, Error = ()> {
future::ok(42)
}
对于简单的情况,您只需要调用wait
:
fn main() {
let s = example().wait();
println!("{:?}", s);
}
然而,这伴随着一个非常严重的警告:
此方法不适合在事件循环或类似 I/O 情况下调用,因为它会阻止事件循环继续进行(这会阻塞线程)。只有在保证与此未来相关的阻塞工作将由另一个线程完成时,才应调用此方法。
东京
如果您使用的是 Tokio 0.1,则应使用 Tokio 的Runtime::block_on
:
use tokio; // 0.1.21
fn main() {
let mut runtime = tokio::runtime::Runtime::new().expect("Unable to create a runtime");
let s = runtime.block_on(example());
println!("{:?}", s);
}
如果您查看 的实现block_on
,它实际上会将未来的结果发送到一个通道,然后调用wait
该通道!这很好,因为 Tokio 保证将未来运行到完成。
也可以看看:
推荐阅读
- python - SQLite 将每一行除以另一个表中同一行的值
- python - 如何设置 if 语句以使用条件数组作为 python 中的输入
- python - 将用 python 训练的 XGBoost 模型移植到另一个用 C/C++ 编写的系统
- javascript - 如何为列表的每条记录添加 data-test-id
- node.js - 使用猫鼬查询计算交易项目总数和交易项目
- javascript - 匹配心脏表情符号字符的 JavaScript 正则表达式行为怪异
- r - 绘制矩阵 ggplot2 的行
- java - 只收集第一个索引
- python - 打印包含用户定义对象的列表返回地址并在 python 中实现复数的根
- javascript - Keccak -f Round constants 十六进制转二进制不是一个位