37

我正在尝试使用 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()未来之前我未能正确启动任务,但我找不到如何正确执行它,假设它甚至是可能的。

4

3 回答 3

67

标准库期货

让我们用它作为我们最小的、可重现的例子

async fn example() -> i32 {
    42
}

致电executor::block_on

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 保证将未来运行到完成。

也可以看看:

于 2018-09-26T15:41:32.310 回答
0

这适用于我使用 tokio:

tokio::runtime::Runtime::new()?.block_on(fooAsyncFunction())?;
于 2021-02-23T08:09:20.070 回答
0

由于这是查询“如何在 Rust 中从同步中调用异步”在搜索引擎中出现的最高结果,我决定在这里分享我的解决方案。我认为它可能有用。

正如@Shepmaster 所提到的,在 0.1 版本的futurescrate 中,有漂亮的方法.wait()可以用来从同步函数中调用异步函数。但是,这个必备方法已从 crate 的更高版本中删除。

幸运的是,重新实现它并不难:

trait Block {
    fn wait(self) -> <Self as futures::Future>::Output
        where Self: Sized, Self: futures::Future
    {
        futures::executor::block_on(self)
    }
}

impl<F,T> Block for F
    where F: futures::Future<Output = T>
{}

之后,您可以执行以下操作:

async fn example() -> i32 {
    42
}

fn main() {
    let s = example().wait();
    println!("{:?}", s);
}

请注意,这带有.wait()@Shepmaster 的答案中解释的所有原始警告。

于 2021-10-21T19:18:53.363 回答