Большинство методов должно возвращать Task, поскольку при потреблении Task возникает меньше скрытых ловушек, чем при потреблении ValueTask. Подробности см. в рецепте 2.11
Возможны ситуации (например, с Task.WhenAll), в которых Task может содержать несколько исключений, а await повторно выдает только первое из них. За примером обработки всех исключений обращайтесь к рецепту 2.4
Пояснение Task.Delay неплохо подходит для модульного тестирования асинхронного кода или реализации логики повторных попыток. Но если нужно реализовать тайм-аут, лучшим кандидатом будет CancellationToken
Другой антипаттерн Task.WhenAny — обработка задач по мере их завершения. Сначала может показаться разумным вести список задач и удалять каждую задачу из списка при завершении. Проблема в том, что такое решение выполняется за время O(N2), хотя существует алгоритм со временем O(N). Правильный алгоритм O(N) рассматривается в рецепте 2.6
рецепте 2.5 рассматривается ожидание завершения любой задачи из группы задач. В рецепте 2.6 рассматривается ожидание завершения коллекции задач с выполнением действий при завершении каждой задачи. В рецепте 2.8 рассматривается обработка исключений для методов asyncTask
Подписки ведут себя по-разному с холодными и горячими наблюдаемыми объектами. Горячий (hot) наблюдаемый объект представляет собой поток событий, который всегда находится в движении, и, если при появлении события нет ни одного подписчика, оно теряется. Например, перемещение мыши является горячим наблюдаемым событием. У холодного (cold) наблюдаемого объекта события не поступают постоянно. Холодный наблюдаемый объект реагирует на подписку, начиная последовательность событий. Например, загрузка HTTP является холодным наблюдаемым объектом; подписка инициирует отправку запроса HTTP. Оператор
Если имеется операция, создающая интенсивную нагрузку на процессор, которую вы хотели бы рассматривать как асинхронную (например, чтобы она не блокировала UI-поток), обращайтесь к главе 4 и рецепту 8.4. Кроме того, в этой главе рассматриваются только операции, которые один раз начинаются и один раз завершаются; если нужно обрабатывать потоки событий, обращайтесь к главам 3 и 6
Оператор Subscribe также всегда должен получать параметр обработки ошибок. В предыдущих примерах этого параметра нет; ниже приведен более правильный пример, который будет правильно реагировать, если наблюдаемый поток завершается с ошибкой: Observable.Interval(TimeSpan.FromSeconds(1)) .Timestamp() .Where(x => x.Value % 2 == 0) .Select(x => x.Timestamp) .Subscribe(x => Trace.WriteLine(x), ex => Trace.WriteLine(ex
Если рефакторинг не дает приемлемого решения, есть альтернатива. Стивен Тауб (Stephen Toub) и Джон Скит (Jon Skeet) разработали методы расширения, возвращающие массив задач, которые завершаются по порядку. Решение Стивена Тауба (Stephen Toub) доступно в блоге Parallel Programming with .NET (https://devblogs.microsoft.com/pfxteam/processing-tasks-as-they-complete/), а решение Джона Скита (Jon Skeet) — в его блоге, посвященном программированию (https://codeblog.jonskeet.uk/2012/01/16/eduasync-part-19-ordering-by-completion-ahead-of-time