Wednesday 27 May 2020

Async Await Reference Implementation

Issue # 1 - async method is called in a void method



InCorrect Usage:
        public static async Task FooAsync()
        {
            // some async code here...
            await Task.Delay(10000);
        }

           public void ThisWillNotWaitForAsyncCodeToComplete()
        {
            try
            {
                 Console.WriteLine("Before : " + DateTime.Now.ToString());
                FooAsync();
                Console.WriteLine("After : " + DateTime.Now.ToString());
            }
            catch (Exception ex)
            {
                //The below line will never be reached
                Console.WriteLine(ex.Message);
            }
        }


Correct Usage:
        public static async Task FooAsync()
        {
            await Task.Delay(10000);
        }

           public async Task ThisWillNotWaitForAsyncCodeToCompleteAsync()
        {
                Console.WriteLine("Before : " + DateTime.Now.ToString());
                await FooAsync();
                Console.WriteLine("After : " + DateTime.Now.ToString());
         }


Changes to be noted carefully:
  1. FooAsync() is called using await.
  2. Method having async Task in the signature

Result:
  1. Async / await chain is followed properly. 
  2. Parent thread will wait for the child thread to complete. In case of any database or event operations being performed, parent thread will wait for the completion before quitting.
  3. SEVERITY Level = FATAL

Issue # 2 - no “async” keyword used and “task” object is returned 



InCorrect Usage:
             public static Task BarAsync()
        {
            // some async code here...
            return Task.Delay(10000);
        }

                public void ThisWillNotWaitForAsyncCodeToComplete()
        {
            try
            {
                Console.WriteLine("Before : " + DateTime.Now.ToString());
                BarAsync();
                Console.WriteLine("After : " + DateTime.Now.ToString());
            }
            catch (Exception ex)
            {
                //The below line will never be reached
                Console.WriteLine(ex.Message);
            }
        }


Correct Usage:
                public static async Task BarAsync()
        {
            await Task.Delay(10000);
        }

                public async Task ThisWillNotWaitForAsyncCodeToCompleteAsyncAsync()
        {
                Console.WriteLine("Before : " + DateTime.Now.ToString());
                await BarAsync();
                Console.WriteLine("After : " + DateTime.Now.ToString());
         }


Changes to be noted carefully:
  1. FooAsync() is using async and not returning task.
  2. ThisWillNotWaitForAsyncCodeToCompleteAsync() is calling FooAsync() using await.

Result:
  1. Async / await chain is followed properly. 
  2. Parent thread will wait for the child thread to complete. In case of any database or event operations being performed, the parent thread will wait for the completion before quitting.
  3. SEVERITY Level = FATAL

Issue # 3 - Method is marked as “async Task” but no async method is called inside.



InCorrect Usage:
Public void Foo()
{}
Public void Bar()
{}

            public async Task FakeAsyncMethod()
      {
       Foo();
Bar();
Return Task.CompletedTask;
      }



Correct Usage:
Public void Foo()
{}
Public void Bar()
{}
               public void FakeAsyncMethod()
         {
                 Foo();
Bar();
         }
Changes to be noted carefully:
  1. Remove async task from the FakeAsyncMethod()
  2. Removed return task statement.


Result:
  1. Normally the sync method should be called as expected.
  2. SEVERITY level = Important

Issue # 4 - async has blocking call instead of using await



InCorrect Usage : 
        public static async Task FooAsync()
        {
            await Task.Delay(10000);
        }

                public void ThisWillNotWaitForAsyncCodeToCompleteAsync()
        {
                Console.WriteLine("Before : " + DateTime.Now.ToString());
                FooAsync().Result;
                Console.WriteLine("After : " + DateTime.Now.ToString());
        }


Correct Usage : 
public static async Task FooAsync()
         {
            await Task.Delay(10000);
         }

        public void ThisWillNotWaitForAsyncCodeToCompleteAsync()
        {
                Console.WriteLine("Before : " + DateTime.Now.ToString());
                await FooAsync();
                Console.WriteLine("After : " + DateTime.Now.ToString());
         }
Changes to be noted carefully:
  1. added await in FooAsync() to follow the appropriate async/await chain.
  2. Using .Result deprives off the async benefit

Result:
  1. async method should be called as per the recommendation and the correct way of doing.
  1. SEVERITY level = Important

Issue # 5 - Blocking async method with .Wait



InCorrect Usage : 

public async Task FooAsync(string id)
{
… some more function code without any await operation inside…
await Task.Delay(10000);
}

public void Bar()
{
console.writeline(“Hello world”);
FooAsync().Wait();
}


Correct Usage : 
          public async Task FooAsync(string id)
{
… some more function code without any await operation inside…
await Task.Delay(10000);
}

public async Task BarAsync()
{
console.writeline(“Hello world”);
await FooAsync();
}
Changes to be noted carefully:
  1. Use async await in BarAsync() to follow the appropriate async/await pattern.


Result:
  1. async method should be called as per the way they are supposed to rather than forcefully converting them to sync and blocking the current thread.
  1. SEVERITY level = Important

Issue # 6 - Create task for sync method and waiting on the task.



InCorrect Usage : 

public void SomeMethod1()
{
… some function code ….

Var task = Task.Run(()=>SomeMethod2);
task.Wait();

… some functional code….
}

public void SomeMethod2()
{
… Some function code goes here ...
}


Correct Usage : 
           public void SomeMethod1()
{
… some function code ….

SomeMethod2();

… some functional code….
}

public void SomeMethod2()
{
… Some function code goes here ...
}

Changes to be noted carefully:
  1. Use sync method normally the way it is supposed to use. Making a task and then waiting on the task is just wasting an additional thread on the pool when the same work can be done the main thread itself.

Result:
  1. Optimized performance rather than bloating CPU for creation of task when method has to be waited anyway.
  1. SEVERITY level = Important

Issue # 7 - Retrieving result of multiple tasks



InCorrect Usage : 

public async Task<string> FooAsync()
{
string result = string.empty;
… some function code…
return result;
}
public async Task<string> BarAsync()
{
string result = string.empty;
… some function code…
return result;
}


public void ParentMethod()
{
var task1 = FooAsync();
var task2 = BarAsync();
Task.WaitAll(task1,task2);
}


Correct Usage : 
public async Task<string> FooAsync()
{
string result = string.empty;
… some function code…
return result;
}
public async Task<string> BarAsync()
{
string result = string.empty;
… some function code…
return result;
}


public async Task ParentMethod()
{
var task1 = FooAsync();
var task2 = BarAsync();
await task.WhenAll(task1,task2);
}

Changes to be noted carefully:
  1. We should avoid mixing blocking & unblocking code. Task.WaitAll is blocking call whereas Task.WhenAll is nonblocking and maintains the async semantics.

Result:
  1. Code is optimized and works as per the async / await programming guidelines of avoiding blocking calls.
  2. SEVERITY level = Important

Issue # 8 - Sync version used when Async is available.

InCorrect Usage :


   public bool CheckLabelAlreadyExist(string labelName, Guid facilityKey, int labelTypeCode)
        {
            return GetQueryable().Any(x => x.DescriptionText == labelName && x.FacilityKey == facilityKey && x.LabelTypeCode == labelTypeCode);
        }
CorrectUsage:
   public async Task<bool> CheckLabelAlreadyExist(string labelName, Guid facilityKey, int labelTypeCode)
        {
            return await GetQueryable().AnyAsync(x => x.DescriptionText == labelName && x.FacilityKey == facilityKey && x.LabelTypeCode == labelTypeCode);
        }
Task Waiting General Rules (phase 1):
To Do This …
Instead of This …
Use This
Retrieve the result of a background task
Task.Wait, Task.Result or Task.GetAwaiter.GetResult
await
Wait for any task to complete
Task.WaitAny
await Task.WhenAny
Retrieve the results of multiple tasks
Task.WaitAll
await Task.WhenAll
Wait a period of time
Thread.Sleep
await Task.Delay

Method Naming Conventions:
  1. All async methods should have “async” suffix in the method name for easy readability and differentiation between sync and async methods. 
  2. Having “async” in the methods, make it more prominent and reduces the chances of error in implementation.
  3. This can be done in phase 2

References :

This brought under the Guidence of Vishal Sharma (Solution Architech)