rust - 如何同步返回在异步 Future 中计算的值?

标签 rust async-await future

我正在尝试使用 hyper 来获取 HTML 页面的内容,并希望同步返回 future 的输出。我意识到我可以选择一个更好的例子,因为同步 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() 出现 panic ,并显示以下错误消息:

Content = Err("Error scraping web page: Error { kind: Execute, cause: None }")

我知道我在将来调用 .wait() 之前未能正确启动任务,但我找不到如何正确地执行它,假设它甚至是可能的。

最佳答案

标准库 future

让我们用它作为我们的 minimal, reproducible example :

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 .

future 0.1

让我们用它作为我们的 minimal, reproducible example :

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);
}

但是,这带有一个非常严重的警告:

This method is not appropriate to call on event loops or similar I/O situations because it will prevent the event loop from making progress (this blocks the thread). This method should only be called when it's guaranteed that the blocking work associated with this future will be completed by another thread.

东京

如果你使用的是 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 的实现,它实际上将 future 的结果发送到一个 channel ,然后在该 channel 上调用 wait!这很好,因为 Tokio 保证运行 future 直到完成。

另见:

关于rust - 如何同步返回在异步 Future 中计算的值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52521201/

相关文章:

javascript - 如何在 JavaScript 中延迟重试异步函数?

java - 为什么使用随机和线程池时输出顺序没有改变?

scala - 在 Scala 中结合 scalacheck Gen 和 Future

rust - reqwest示例POST请求未编译

rust - 使用静态 Fn 函数初始化 FnMut 成员变量

rust - 使用类型作为不同的名称和类型别名有什么区别?

r - 如何在 R 中执行并行异步 POST API 调用?

rust - Rust:将两个Vecs映射到复合结构的第三个Vec中

c# - 调用异步方法而不等待应答

javascript - Nodejs如何使用多个await promise