DEV Community

Ellie
Ellie

Posted on

Async Programming in C#

Asynchronous programming is needed to write code that can handle a long-running requests (like querying a database, calling an API, or reading a large file) instead of just stopping and waiting for response.
This Async Programming is essential for having a responsive application. The UI freeze without using async in mobile app and using async programming on web servers allows handling a massive number of requests instead of being blocked by one slow request.

using sync means the main thread will be blocked till it finish the current task.

sync programming
while via async the main thread won't get blocked and is immediately freed up to do other things. Whenever the task finishes, the main thread will pick up the result and continue.

async programming

The Analogy: Doing Chores

Imagine it's a weekend and you have to do some Cores at home. you might need to do laundries, washing some dishes and probably you want to watch a new episode of your favourite series. So you probably first load washer machine and start the washing cycle and while machine is washing the laundries you might fill up the dish washer and start the machine. While both machines are working so you have time for watching a new episode.
all the step above you will do them async. It means that you are not standing in front of washer doing nothing till the cycle finish. you have just start the task and then you are free to do some other tasks. you are the main thread and by starting each machine you starting a task and free up the main thread.

Syntax at a glance

Here is the example using analogy described above

You only need to know three main things.

Task: The promise
This object represents the work-in-progress. Think of it as a "receipt" we get from the washer. It's not the clean clothes, but it's the promise of clean clothes.

  • Task: Use this for an async method that doesn't return a value (like void).
  • Task<T>: Use this for an async method that does return a value. (e.g., Task promises to give you a string eventually).

async: The marker
We add this keyword to a method signature (e.g., public async Task DoLaundry()). It simply "unlocks" the ability to use the await keyword inside that method. It's just like a signal to the compiler.

await: The "Pause Button"
This is the operator we need to put in front of a Task (e.g., string result = await GetSomeDataAsync();).and It actually tells the compiler that here is a long-running task. Compiler can pause this method here, give control back to the caller, and later when the task is finished it can come back to this exact line to continue.

Scenario: Pricing service

Consider you have a train ticket and you want to calculate the total price for the customers. let's consider for each ticket we have Ticket Price, Insurance price, Vat and Admin Fee that needs to get calculated.

Sync Solution:

so here we have multiple fees need to calculated and each will take couple of seconds:

private int GetTicketPrice()
    {
        Console.WriteLine($" getting Ticket Price at {DateTime.Now}");
        Thread.Sleep(TimeSpan.FromSeconds(0.5));
        return 20;
    }

    private int GetInsurancePrice()
    {
        Console.WriteLine($" getting insurance Price at {DateTime.Now}");
        Thread.Sleep(TimeSpan.FromSeconds(1));
        return 5;
    }

    private int CalculateVat()
    {
        Console.WriteLine($" Calculating Vat at {DateTime.Now}");
        Thread.Sleep(TimeSpan.FromSeconds(5));
        return 1;
    }

    private int CalculateAdminFee()
    {
        Console.WriteLine($" Calculating Admin fee at {DateTime.Now}");
        Thread.Sleep(TimeSpan.FromSeconds(2));
        return 2;
    }
Enter fullscreen mode Exit fullscreen mode

and we have a this Fee Calculator which I've added a Stopwatch to see how much time it will takes to perform:

public class SyncPricingService
{
    public int CalculateFees()
    {
        var watch = Stopwatch.StartNew();

       var  ticketFee = GetTicketPrice();
       var insuranceFee = GetInsurancePrice();
       var vatFee = CalculateVat();
       var adminFee = CalculateAdminFee();

       var fees = new[] { ticketFee, insuranceFee, vatFee, adminFee }; 

       watch.Stop();
       Console.WriteLine($"Total time <CalculateFees>: {watch.ElapsedMilliseconds}ms");

        return fees.Sum();
    }
}
Enter fullscreen mode Exit fullscreen mode

Later we will calculate the fee with sync approach by simply using syncPricingService.CalculateFees();

Here is the flow which will take 8.5 second:

Sync Fee Calculator flow

▶️ Runnable sample: Sync Fee Calculator

Async Solution:

again we have multple functions for calculating fees:

private async Task<int> GetTicketPriceAsync()
    {
        Console.WriteLine($" getting Ticket Price at {DateTime.Now}");
        await Task.Delay( TimeSpan.FromSeconds(0.5));
        return 20;
    }

    private async Task<int> GetInsurancePriceAsync()
    {
        Console.WriteLine($" getting Insurance Price at {DateTime.Now}");
        await Task.Delay( TimeSpan.FromSeconds(1));
        return 5;
    }

    private async Task<int> CalculateVatAsync()
    {
        Console.WriteLine($" Calculating Vat at {DateTime.Now}");
        await Task.Delay( TimeSpan.FromSeconds(5));
        return 1;
    }

    private async Task<int> CalculateAdminFeeAsync()
    {
        Console.WriteLine($" Calculating Admin fee at {DateTime.Now}");
        await Task.Delay( TimeSpan.FromSeconds(2));
        return 2;
    }
Enter fullscreen mode Exit fullscreen mode

and here we have this Fee Calculator:

public class AsyncPricingService
{
    public async Task<int> CalculateFeesAsync()
    {
        var watch = Stopwatch.StartNew();

        var  ticketFeeTask = GetTicketPriceAsync();
        var insuranceFeeFeeTask =  GetInsurancePriceAsync();
        var vatFeeTask = CalculateVatAsync();
        var adminFeeTask = CalculateAdminFeeAsync();

        var fees = await Task.WhenAll( ticketFeeTask, insuranceFeeTask, vatFeeTask, adminFeeTask );

        watch.Stop();
        Console.WriteLine($"Total time <CalculateFeesAsync>: {watch.ElapsedMilliseconds}ms");



        return fees.Sum();
    }
}
Enter fullscreen mode Exit fullscreen mode

Later we can simply calculate the fee with async approach by calling await asyncPricingService.CalculateFeesAsync();

Here is the flow which will take 5 second:

Async Fee Calculator flow

▶️ Runnable sample: Async Fee Calculator

Top comments (0)