Home > Software design >  How do you get multiple urls at the same time in a synchronus function
How do you get multiple urls at the same time in a synchronus function

Time:12-12

I am getting data from the open weather map API. Currently the data is being retrieved synchronously which is slow. However, the function has to be synchronous as it is part of a library, but it can call an async function. How might I still make concurrent requests to increase performance? A solution that does not use reqwests works, but reqwests is preferred.

fn get_combined_data(open_weather_map_api_url: String, open_weather_map_api_key: String,
                           coordinates: Vec<String>, metric: bool) -> Vec<HashMap<String, String>> {
    let urls: Vec<String> = get_urls(open_weather_map_api_url, open_weather_map_api_key,
                      coordinates.get(0).expect("Improper coordinates").to_string()   ","  
                          coordinates.get(1).expect("Improper coordinates"), metric);
    let mut data: Vec<HashMap<String, String>> = Vec::new();
    for url in urls {
        let request = reqwest::blocking::get(url).expect("Url Get failed").json().expect("json expected");
        data.push(request);
    }
    return data;
}

CodePudding user response:

The easiest is probably to use tokios new_current_thread runtime and blocking on the data retreival.

use std::collections::HashMap;
use tokio::runtime;
pub fn collect_data() -> Vec<HashMap<String, String>> {
    let rt = runtime::Builder::new_current_thread()
        .build()
        .expect("couldn't start runtime");
    let urls = vec!["https://example.com/a", "https://example.com/b"];
    rt.block_on(async move {
        let mut data = vec![];
        for url in urls {
            data.push(async move {
                reqwest::get(url)
                    .await
                    .expect("Url Get Failed")
                    .json()
                    .await
                    .expect("json expected")
            });
        }
        futures::future::join_all(data).await
    })
}

CodePudding user response:

You need an asynchronous runtime in order to call asynchronous functions. The easiest way to get one is to use the #[tokio::main] attribute (which despite the name can be applied to any function):

#[tokio::main]
fn get_combined_data(
    open_weather_map_api_url: String,
    open_weather_map_api_key: String,
    coordinates: Vec<String>,
    metric: bool,
) -> Vec<HashMap<String, String>> {
    let urls: Vec<String> = get_urls(
        open_weather_map_api_url,
        open_weather_map_api_key,
        coordinates
            .get(0)
            .expect("Improper coordinates")
            .to_string()
              ","
              coordinates.get(1).expect("Improper coordinates"),
        metric,
    );
    futures::future::join_all (urls.map (|u| {
        async move {
                reqwest::get(url)
                    .await
                    .expect("Url Get Failed")
                    .json()
                    .await
                    .expect("json expected")
            }
    })).await
}
  • Related