Multiple actix-web client requests - expected struct actix_web::Error found ()

I got this:

extern crate actix;
extern crate actix_web;
extern crate env_logger;
extern crate futures; // 0.1.25
extern crate tokio; // 0.1.17
use futures::future::ok as fut_ok;
use futures::Future;

extern crate serde_json;

use std::cell::RefCell;
use std::rc::Rc;
use tokio::runtime::current_thread::Runtime;

type Error = ();

fn questions_data(
    val: i32,
) -> Box<Future<Item = serde_json::Value, Error = actix_web::error::Error>> {
    use actix_web::{client, HttpMessage};
    use std::time::Duration;
    Box::new(
        client::ClientRequest::get(
            "https://jsonplaceholder.typicode.com/todos/1".to_owned() + &val.to_string(),
        )
        .finish()
        .unwrap()
        .send()
        .timeout(Duration::from_secs(30))
        .map_err(actix_web::error::Error::from) // <- convert SendRequestError to an Error
        .and_then(|resp| {
            resp.body().limit(67_108_864).from_err().and_then(|body| {
                let resp: serde_json::Value = serde_json::from_slice(&body).unwrap();
                fut_ok(resp)
            })
        }),
    )
}

fn main() {
    let results: Rc<RefCell<Vec<serde_json::Value>>> = Rc::new(RefCell::new(Vec::new()));
    let mut rt = Runtime::new().unwrap();
    let sys = actix::System::new("asyncio_example");
    for n in 1..100 {
        let res = results.clone();
        rt.spawn(
            questions_data(n)
                .map_err(|e| println!("Error: {}", e))
                .map(move |n| {
                    println!("n: {:?}", n);
                    res.borrow_mut().push(n);
                }),
        );
    }
    let _ = sys.run();
    rt.run();
    println!("{:?}", results.borrow().last());
}

But nothing happens :confused:

It looks like you are basically trying to do this: https://github.com/actix/examples/blob/master/http-proxy/src/main.rs

So I got this code:

extern crate actix;
extern crate actix_web;
extern crate env_logger;
extern crate futures; // 0.1.25
extern crate tokio; // 0.1.17
use futures::future::ok as fut_ok;
use futures::stream;
use futures::Future;
use futures::Stream;

use actix::Arbiter;
extern crate serde_json;
use actix_web::{client, HttpMessage};
use std::time::Duration;

use std::cell::RefCell;
use std::rc::Rc;
fn questions_data(
    val: i32,
) -> Box<Future<Item = serde_json::Value, Error = actix_web::error::Error>> {
    Box::new(
        client::ClientRequest::get(
            "https://jsonplaceholder.typicode.com/todos/1".to_owned() + &val.to_string(),
        )
        .finish()
        .unwrap()
        .send()
        .timeout(Duration::from_secs(30))
        .map_err(actix_web::error::Error::from) // <- convert SendRequestError to an Error
        .and_then(|resp| {
            resp.body().limit(67_108_864).from_err().and_then(|body| {
                let resp: serde_json::Value = serde_json::from_slice(&body).unwrap();
                fut_ok(resp)
            })
        }),
    )
}

fn main() {
    let sys = actix::System::new("asyncio_example");

    let results: Rc<RefCell<Vec<serde_json::Value>>> = Rc::new(RefCell::new(Vec::new()));
    for n in 1..10 {
        let res = results.clone();
        Arbiter::spawn(
            questions_data(n)
                .map_err(|e| println!("Error: {}", e))
                .map(move |n| {
                    println!("n: {:?}", n);
                    res.borrow_mut().push(n);
                }),
        );
    }
    let _ = sys.run();
    println!("{:?}", *results);
}

It compiles finally.

But it never run the final println!:

n: Object({"completed": Bool(false), "id": Number(13), "title": String("et doloremque nulla"), "userId": Number(1)})
n: Object({"completed": Bool(true), "id": Number(14), "title": String("repellendus sunt dolores architecto voluptatum"), "userId": Number(1)})
n: Object({"completed": Bool(true), "id": Number(16), "title": String("accusamus eos facilis sint et aut voluptatem"), "userId": Number(1)})
n: Object({"completed": Bool(true), "id": Number(12), "title": String("ipsa repellendus fugit nisi"), "userId": Number(1)})
n: Object({"completed": Bool(true), "id": Number(15), "title": String("ab voluptatum amet voluptas"), "userId": Number(1)})
n: Object({"completed": Bool(true), "id": Number(17), "title": String("quo laboriosam deleniti aut qui"), "userId": Number(1)})
n: Object({"completed": Bool(false), "id": Number(18), "title": String("dolorum est consequatur ea mollitia in culpa"), "userId": Number(1)})
n: Object({"completed": Bool(true), "id": Number(19), "title": String("molestiae ipsa aut voluptatibus pariatur dolor nihil"), "userId": Number(1)})
n: Object({"completed": Bool(true), "id": Number(11), "title": String("vero rerum temporibus dolor"), "userId": Number(1)})

I have change from using Arbiter::spawn to actix::spawn as suggested by @DoumanAsh, but now no request are made.

extern crate actix;
extern crate actix_web;
extern crate env_logger;
extern crate futures; // 0.1.25
extern crate tokio; // 0.1.17
use futures::future::ok as fut_ok;
use futures::Future;

extern crate serde_json;
use actix_web::{client, HttpMessage};
use std::time::Duration;

use std::cell::RefCell;
use std::rc::Rc;
fn questions_data(
    val: i32,
) -> Box<Future<Item = serde_json::Value, Error = actix_web::error::Error>> {
    Box::new(
        client::ClientRequest::get(
            "https://jsonplaceholder.typicode.com/todos/1".to_owned() + &val.to_string(),
        )
        .finish()
        .unwrap()
        .send()
        .timeout(Duration::from_secs(30))
        .map_err(actix_web::error::Error::from) // <- convert SendRequestError to an Error
        .and_then(|resp| {
            resp.body().limit(67_108_864).from_err().and_then(|body| {
                let resp: serde_json::Value = serde_json::from_slice(&body).unwrap();
                fut_ok(resp)
            })
        }),
    )
}

fn main() {
    let sys = actix::System::new("asyncio_example");
    let _ = sys.run();

    let results: Rc<RefCell<Vec<serde_json::Value>>> = Rc::new(RefCell::new(Vec::new()));
    for n in 1..10 {
        let res = results.clone();
        actix::spawn(
            questions_data(n)
                .map_err(|e| println!("Error: {}", e))
                .map(move |n| {
                    println!("n: {:?}", n);
                    res.borrow_mut().push(n);
                }),
        );
    }
    println!("{:?}", *results);
}

I probably still don’t get those actors and tokio.

You have to have sys.run(), because otherwise Actix’s futures do nothing — nothing is running them.

It is intended to run forever. It’s not for running of just your few requests. It’s for ability to handle Actix’s futures from anywhere in the program at any time.

You can send sys over mpsc channel and execute sys.run() on another thread and leave it running there forever. Or keep it in main() and shut it down when you want to exit the program. See how Actix-web spawns and kills it in its unit tests.

You have to do something else to wait for your requests to complete.


Another tip: futures are annoyingly picky about error types, and trying to get them to agree is painful. The easiest solution is to use your own universal error type for everything, and convert every other error everywhere to your error type as soon as you can. quick_error crate makes it easy to auto-add From implementations.