Как мне синхронно вернуть значение, рассчитанное в асинхронном будущем в стабильном Rust? - PullRequest
0 голосов
/ 26 сентября 2018

Я пытаюсь использовать гипер для захвата содержимого 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() в будущем, но не смог 'не могу найти, как правильно сделать это, предполагая, что это даже возможно.

1 Ответ

0 голосов
/ 26 сентября 2018

Стандартные библиотечные фьючерсы

Давайте использовать это как наш минимальный, воспроизводимый пример :

use futures::future; // 0.3.0-alpha.17
use std::future::Future; 

fn example() -> impl Future<Output = i32> {
    future::ready(42)
}

Вызов executor::block_on:

fn main() {
    let v = futures::executor::block_on(example());
    println!("{}", v);
}

Фьючерсы 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);
}

Однако это сопровождается довольно серьезным предупреждением:

Этот метод не подходит для вызова циклов обработки событий или подобных ситуаций ввода / вывода, поскольку он будетпредотвратить цикл обработки событий (это блокирует поток).Этот метод следует вызывать только в том случае, если гарантируется, что работа по блокировке, связанная с этим будущим, будет завершена другим потоком.

Если вы используете Tokio, вам следует использовать 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 гарантирует выполнение будущего до завершения.

См. Также:

...