Speed up multiple API calls

So I did this project in uni that I am trying to refactor. One of the problems I am having is my method for getting the top list which consist of around 250 movies, e.g. 250 API calls. After that I render them all on my web page. The API I am using is OMDBAPI and I am getting every movie individually as you can see in the code below.

Basically that the web page does is as default loads 10 movies but I can also load in all movies which is around 250.

I am trying to wrap my head around asynchronous programming. So basically it is taking around 4-6 seconds to process this method according to stopwatch in C# but I believe it should be possible to refactor and refine. I am new to asynchronous programming and I have tried looking at MSFT documentation and several issues before here on SO, but I am not getting anywhere with speeding up the calls.

I have looked at using parallel for the issue but I think my problem should be solvable with async? With stopwatch in C# I have pinpointed the delay to come mostly from between the two x.

I would foremost like to speed up the calls but I would love tips on best practice with async programming as well.

public async Task<List<HomeTopListMovieDTO>> GetTopListAggregatedData(Parameter parameter)
{
    List<Task<HomeTopListMovieDTO>> tasks = new List<Task<HomeTopListMovieDTO>>();
    var toplist = await GetToplist(parameter);
//x
    foreach (var movie in toplist)
    {
        tasks.Add(GetTopListMovieDetails(movie.ImdbID));
    }
    var results = Task.WhenAll(tasks);
//x
    var tempToplist = toplist.ToArray();
    for (int i = 0; i < tasks.Count; i++)
    {
        tasks[i].Result.NumberOfLikes = tempToplist[i].NumberOfLikes;
        tasks[i].Result.NumberOfDislikes = tempToplist[i].NumberOfDislikes;
    }

    List<HomeTopListMovieDTO> toplistMovies = results.Result.ToList();

    return toplistMovies;
}
public async Task<HomeTopListMovieDTO> GetTopListMovieDetails(string imdbId)
{
    string urlString = baseUrl + "i=" + imdbId + accessKey;
    return await apiWebClient.GetAsync<HomeTopListMovieDTO>(urlString);
}
public async Task<T> GetAsync<T>(string urlString)
{
    using (HttpClient client = new HttpClient())
    {
        var response = await client.GetAsync(urlString,
            HttpCompletionOption.ResponseHeadersRead);
        response.EnsureSuccessStatusCode();
        var data = await response.Content.ReadAsStringAsync();
        var result = JsonConvert.DeserializeObject<T>(data);
        return result;
    }
}

1 answer

  • answered 2021-01-17 13:33 djbobo

    You async code looks OKey. I would throttle it to not make more than X parallel requests using Partitioner / Parallel for each instead but approach with WaitAll is also good enough unless you see connection refused because of port exhaustion or API DDOS protection.

    You should reuse HttpClient, see more details in https://www.aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong, so in your case create HttpClient in the root method and pass it as a parameter to your async methods. HttpClient is thread safe, can be used in parallel calls.

    You should dispose HttpResponse.