Выполнение параллельных вызовов API и асинхронная обработка каждого ответа в C # - PullRequest
0 голосов
/ 04 сентября 2018

У меня есть сценарий, в котором мне нужно сделать несколько вызовов API (один и тот же API с различными параметрами) параллельно в C # (Xamarin iOS и Xamarin Android). И я не хочу ждать завершения всех задач, вместо этого, когда приходит ответ, я должен обработать его и соответствующим образом обновить пользовательский интерфейс.

Метод, который нужно вызывать несколько раз

public  async Task<Response> GetProductsAsync(int categoryId, int pageNo = -1, int pageSize = -1)
        {
            try
            {
                string url = "";
                if (pageNo == -1 || pageSize == -1)
                    url = $"catalog/v1/categories/{categoryId}/products";
                else
                    url = $"catalog/v1/categories/{categoryId}/products?page-number={pageNo}&page-size={pageSize}";
                var response = await client.GetAsync(url);
                string responseString = await response.Content.ReadAsStringAsync();
                GetParsedData(response.IsSuccessStatusCode, responseString);
            }
            catch (Exception e)
            {
                apiResponse.status = "internalError";
                apiResponse.data = e.Message;
            }
            return apiResponse;
        }

Ответы [ 2 ]

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

Это способ ожидания асинхронной работы нескольких задач и обновления пользовательского интерфейса при выполнении любой из них.

async Task GetSomeProductsAsync( IEnumerable<int> categoryIds )
{
    List<Task<Response>> tasks = categoryIds
        .Select( catId => GetProductsAsync( catId ) )
        .ToList();

    while ( tasks.Any() )
    {
        var completed = await Task.WhenAny( tasks );
        tasks.Remove( completed );
        var response = completed.Result;
        // update the ui from this response
    }
}

В качестве примечания:

Вы должны добавить ConfigureAwait(false) к вашему ожидающему коду в GetProducsAsync, чтобы избежать ненужной синхронизации с потоком вызывающего абонента (который будет здесь с пользовательским интерфейсом)

public  async Task<Response> GetProductsAsync(int categoryId, int pageNo = -1, int pageSize = -1)
{
    try
    {
        string url = "";
        if (pageNo == -1 || pageSize == -1)
            url = $"catalog/v1/categories/{categoryId}/products";
        else
            url = $"catalog/v1/categories/{categoryId}/products?page-number={pageNo}&page-size={pageSize}";
        var response = await client.GetAsync(url).ConfigureAwait(false);
        string responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
        GetParsedData(response.IsSuccessStatusCode, responseString);
    }
    catch (Exception e)
    {
        apiResponse.status = "internalError";
        apiResponse.data = e.Message;
    }
    return apiResponse;
}

Подробнее об этом можно прочитать в статье Стивена Клири в блоге: Не блокировать асинхронный код

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

Из вызывающей функции вы можете написать код, как показано ниже

    public void CallingFunctionToGetProductsAsync() {
        Task.Run(async () =>
        {
            var response = await GetProductsAsync(1);
            ProcessResponse(response);
        });

        Task.Run(async () =>
        {
            var response = await GetProductsAsync(2);
            ProcessResponse(response);
        });
    }
...