Базовый вариант использования Task Unwrap

от автора

Введение

Недавно, после нескольких месяцев отсутствия использования .Net/C#, я улучшал существующее приложение .Net/C# WPF, используя .Net Task Parallel Library (TPL).

Но, наивно применяя шаблоны обещаний JavaScript, которые я использовал в предыдущие месяцы, я столкнулся со странной проблемой, которая заставила меня использовать довольно экзотический метод расширения Unwrap.

В этой статье описывается проблема, объясняется ее причина, предлагается исправление с помощью Unwrap и, наконец, представлена ​​более современная версия с парадигмой async/await C# 5.0.

Простой рабочий процесс в JavaScript с Promises

Вот JavaScript-реализация простого рабочего процесса, состоящего из трех шагов, второй из которых имитирует отложенную обработку с помощью setTimeout с использованием Promise API:

function doFirstThing() { return new Promise(resolve => { console.log("First thing done") resolve() }) }  function doSecondThing() { return new Promise(resolve => { setTimeout(() => { console.log("Second thing done") resolve() }, 1000) }) }  function doThirdThing() { return new Promise(resolve => { console.log("Third thing done") resolve() }) }  doFirstThing().then(doSecondThing).then(doThirdThing)

Вот результат после запуска с Node:

$ node test.js First thing done Second thing done Third thing done

Реализация C# с задачами

Вот тот же рабочий процесс, реализованный на C# с использованием .Net TPL:

using System; using System.Threading.Tasks;  namespace Test {     class Program     {         static Task DoFirstThing()         {             return Task.Run(() => Console.WriteLine("First thing done"));         }          static Task DoSecondThing()         {             return Task.Delay(1000).ContinueWith(_ => Console.WriteLine("Second thing done"));         }          static Task DoThirdThing()         {             return Task.Run(() => Console.WriteLine("Third thing done"));         }          static void Main(string[] args)         {             DoFirstThing().ContinueWith(_ => DoSecondThing()).ContinueWith(_ => DoThirdThing());              Console.ReadLine();         }     } }

Обратите внимание, что в отличие от обещаний JavaScript, задачи .Net не запускаются/планируются автоматически при создании, поэтому необходимо явно вызывать Run.

Here is the result:

First thing done Third thing done Second thing done

Как видите, третий шаг выполняется раньше второго!

Это связано с тем, что ContinueWith создает новую задачу, обертывающую предоставленную обработку, которая состоит только в вызове DoSecondThing (который сам создает вторую задачу), который немедленно возвращает результат.

ContinueWith не будет учитывать результирующую задачу, в отличие от Promise.then, который обрабатывает случай возврата обещания определенным образом: обещание, возвращенное к тому времени, будет разрешено только тогда, когда будет выполнено базовое обещание.

Unwrap в помощь

Чтобы получить поведение обещаний JavaScript, нам нужно явно сообщить TPL, что мы хотим рассмотреть базовую задачу, используя Unwrap (реализованный как метод расширения, предоставляемый классом TaskExtensions):

DoFirstThing()   .ContinueWith(_ => DoSecondThing())   .Unwrap()   .ContinueWith(_ => DoThirdThing()); 

Результат теперь соответствует JavaScript:

First thing done Second thing done Third thing done

Более современный способ реализации await

В C# 5.0 добавлен некоторый синтаксический сахар, чтобы упростить использование TPL с оператором ожидания (await operator ):

await DoFirstThing(); await DoSecondThing(); await DoThirdThing();

await внутренне вызывает Unwrap и ожидает выполнения базовой задачи, как и ожидалось, и дает тот же результат.

Обратите внимание, что await можно использовать только в асинхронном методе.

Заключение

Сопоставление между языками и платформами не всегда очевидно, но, к счастью, в настоящее время все они копируют друг друга и в конечном итоге предлагают одни и те же парадигмы и API, такие как дуэт async/await, который вы используете почти одинаково как в C#, так и в JavaScript.


ссылка на оригинал статьи https://habr.com/ru/articles/806295/