Продолжение задачи <T> для задачи <U> без блокировки результата

У меня есть два асинхронных метода Task<int> DoInt() и Task<string> DoString(int value). У меня есть третий асинхронный метод Task<string> DoBoth(int value), целью которого является асинхронное выполнение DoInt(), выводящий его на DoString(int), и результат будет результатом DoBoth().

Важными ограничениями являются:

  • Возможно, у меня нет исходного кода для DoInt() или DoString(), поэтому я не могу измените их.
  • Я не хочу блокировать в любой момент
  • Результат (результат) одной задачи должен быть передан в качестве входных данных для следующего
  • Конечный результат (результат) - это то, что я хочу быть результатом DoBoth()

Ключом у меня уже есть методы, которые являются асинхронными (т.е. return Task), и я хочу передать данные из задачи в задачу без блокировки по пути, возвращая конечный результат в исходную задачу. Я могу сделать все следующее, кроме как блокирование в следующем коде:

Пример кода:

// Two asynchronous methods from another library, i.e. can't be changed
Task<int> DoInt(); 
Task<string> DoString(int value);

Task<string> DoBoth()
{
    return DoInt().ContinueWith<string>(intTask => 
    {
        // Don't want to block here on DoString().Result
        return DoString(intTask.Result).Result; 
    });
}

Так как Task<string> DoString(int) уже является асинхронным методом, как я могу создать для него неблокирующее продолжение? Фактически я хочу создать продолжение из существующей задачи, а не из Func.

Ответ 1

Вы можете написать всю цепочку, используя TaskExtensions.Unwrap как:

Task<string> DoBoth(int value)
{
    Task<Task<string>> task = DoInt(value).ContinueWith(valueTask => 
    {
        return DoString(valueTask.Result); 
    });

    return task.Unwrap();
}

Обратите внимание, что это предполагает, что DoInt определяется как Task<int> DoInt(int value);, который отличается от вашего описания, но следует вашему примеру кода.

Если DoInt не принимает аргумент int (соответствующий вашим объявлениям), это может стать:

Task<string> DoBoth()
{
    return DoInt().ContinueWith(t => DoString(t.Result)).Unwrap();
}

В качестве дополнительного - используя С# 5 и .NET 4.5 (или пакет асинхронного таргетинга), вы можете записать это как:

async Task<string> DoBoth(int value)
{
    int first = await DoInt(value);
    return await DoString(first);
}