Saturday, 6 May 2017

Async/Await 01

The async await keywords are syntactic sugar that leverage the TPL, in essence all they are is indicators that mark code positions from where control should resume after the completion of a task or thread. If we look at the following example

using System;
using System.Threading.Tasks;

namespace pc.asyncAwaitSimple00
{
    class DataService
    {
        static Random rnd = new Random(DateTime.Now.Millisecond);

        public void UsingTask()
        {
            Console.WriteLine("Started using Task");
            Task.Delay(rnd.Next(1000, 5000)).ContinueWith(
                tr => Console.WriteLine("Ran using Task"));
        }

        public async void UsingAsyncAwait()
        {
            Console.WriteLine("started using async/await");
            await Task.Delay(rnd.Next(1000, 5000));
            Console.WriteLine("Ran using async/await");
        }

    }
    class Program
    {
        static void Main(string[] args)
        {
            int option = -1;
            var ds = new DataService();
            do{
                switch (option){
                    case 1: ds.UsingTask(); break;
                    case 2: ds.UsingAsyncAwait(); break;
                }
                Console.WriteLine("\n0)Exit\n1)Task\n2)async/await");

            } while (int.TryParse(Console.ReadKey().KeyChar.ToString(), out option) && option != 0);
        }
    }
}


you can see the difference in complexity between fetching data with just the Task class, vs leveraging the async/await keywords.

let's take a look at a slightly more involved example, this time our fetch data functions will return values.

using System;
using System.Threading.Tasks;

namespace pc.asyncAwaitSimpleExample
{
    class DataService {
        static Random rnd = new Random(DateTime.Now.Millisecond);

        public Task<string> GetData1() {
            return Task.Run(()=>getData()).ContinueWith(tr => {
                Console.WriteLine(tr.Result);
                return tr.Result;
            });
        }

        string getData() {
            Task.WaitAll(Task.Delay(rnd.Next(1000, 5000)));
            return "my data 1";
        }

        public Task<string> GetData2() {
            return Task.Run(() => {
                Task.WaitAll(Task.Delay(rnd.Next(1000, 5000)));
                return "my Data 2";
            }).ContinueWith(tr => {
                Console.WriteLine(tr.Result);
                return tr.Result;
            });
        }

        public async Task<string> GetData3() {
            await Task.Delay(rnd.Next(1000, 5000));
            string result =  await Task.Run(() =>"my Data 3");
            Console.WriteLine(result);
            return result;
        }
    }
    class Program {
        static void Main(string[] args) {
            int option = -1;
            var ds = new DataService();
            Console.WriteLine("\n0) Exit\n1)Task 1\n2)Task 2\n3)Async/await ");
            do {
                switch (option){
                    case 1: ds.GetData1(); break;
                    case 2: ds.GetData2(); break;
                    case 3: ds.GetData3(); break;
                }
               
            } while (int.TryParse(Console.ReadKey().KeyChar.ToString(), out option) && option != 0);
        }
    }
}


again we see that the async/await keywords greatly simplify the syntax, this is a bit of a contrived example to try and emphasize that the await is basically creating a task with a continue with, firstly however let's take a look at how we'd leverage our return types in our main thread.

using System;
using System.Threading.Tasks;

namespace pc.asyncAwaitSimpleExample
{
    class DataService {
        static Random rnd = new Random(DateTime.Now.Millisecond);

        public Task<string> GetData1() {
            return Task.Run(() => getData());
        }

        string getData() {
            Task.WaitAll(Task.Delay(rnd.Next(1000, 5000)));
            return "my data 1";
        }

        public Task<string> GetData2() {
            return Task.Run(() => {
                Task.WaitAll(Task.Delay(rnd.Next(1000, 5000)));
                return "my Data 2";
            });
        }

        public async Task<string> GetData3() {
            await Task.Delay(rnd.Next(1000, 5000));
            return await Task.Run(() =>"my Data 3");
        }
    }
    class Program {
        static void Main(string[] args) {
            int option = -1;
            var ds = new DataService();
            Console.WriteLine("\n0) Exit\n1)Task 1\n2)Task 2\n3)Async/await ");
            do {
                switch (option){
                    case 1:
                        Task.Run(() => Console.WriteLine(ds.GetData1().Result));
                        break;
                    case 2:
                        Task.Run(() => Console.WriteLine(ds.GetData2().Result));
                        break;
                    case 3:
                        Task.Run(() => Console.WriteLine(ds.GetData3().Result));
                        break;
                }
               
            } while (int.TryParse(Console.ReadKey().KeyChar.ToString(), out option) && option != 0);
        }
    }
}


we have to use tasks in order not to block the main thread while we're fetching our results.

Let's take a look at the following Task implemented function

public void GetData()
{
    Task.Run(() => {
        Thread.Sleep(1000);
        return "My Data";
    }).ContinueWith(
        tr => {
            Console.WriteLine(tr.Result);
        });
} 

it's equivalent is

public async void GetDataAsync()
{
    var data = await Task.Run(() => {
        Thread.Sleep(1000);
        return "my data";
    });

    Console.WriteLine(data);

}

All the async/await keywords really do is give us an easy way to work with continuewiths and make our code look a bit cleaner and easier to follow.