Amit
Amit

Reputation: 1885

Whole function chain needs to be marked async in C#

I have a existing program and I need to call a 3rd party library function that provides only async operations.

Main -> f1() - f2() -> f3() -> f4()

f4 calls an async method of 3rd party. So f4 has to be marked async, then f3 has to be marked async and all the way up to main(). Is that correct understanding or not?

Upvotes: 0

Views: 437

Answers (3)

tmaj
tmaj

Reputation: 35105

A way of thinking about async in c# is to think of it as virus: f4 has been infected and it's only a matter of time before the whole program is async. The difference between a zombie virus and async is that:

  1. there is no cure,
  2. the main characters don't want to stop the virus but instead they encourage you to get infected and
  3. protection looks ugly and often suffers from hidden not-obvious bugs.

Can you stop the spread?

Prior to c# 7.1 main couldn't be async and you'd have to write something like:

public static void Main()
{
    MyAsyncWork().GetAwaiter().GetResult();
}

These days you can just

static async Task Main() // Or async Task<int> Main(string[] args) etc.
{
    return await MyAsyncWork();
}

The language designers acknowledged that async tends to go all the way to the entry point and they provided a way to not have to worry about the async/non-async jump.

However, there is no magic here and when we write static async Task Main() the compiler generates something like

private static void $GeneratedMain() => Main().GetAwaiter().GetResult();

and that's what the 'real' main looks like.

Equipped with this knowledge you could make the async->non-async switch in f4. Doing this has risks and going async all the way often requires only a few minutes of changing method signatures (and optionally their names). Once done you can start leveraging all the benefits of asynchronous in more and more places in your code.

Upvotes: 1

Marc Gravell
Marc Gravell

Reputation: 1063774

The short version is "yes".

The longer version is that you technically can do a few things to avoid that, but: you'll be fighting the language and runtime at every step; it will certainly be a lot more convenient if you simply switch to async/await in that call-path. And yes, for this reason awaitable types are effectively "infectious" - it quickly spreads to more and more if your code. However, this is usually without much (if any) harm, and often has great advantages for scalability. A very benign infection.

Upvotes: 6

Enigmativity
Enigmativity

Reputation: 117154

Here's the situation that you're describing in your question:

async Task Main() { await F1(); }
private async Task F1() { await F2(); }
private async Task F2() { await F3(); }
private async Task F3() { await F4(); }
private async Task F4() { await ThirdParty.LibraryFunction(); }

public class ThirdParty
{
    public static Task LibraryFunction() => Task.Delay(TimeSpan.FromSeconds(1.0));
}

Do I need to await everything. No.

This works just fine:

async Task Main() { await F1(); }
private Task F1() => F2();
private Task F2() => F3();
private Task F3() => F4();
private Task F4() => ThirdParty.LibraryFunction();

public class ThirdParty
{
    public static Task LibraryFunction() => Task.Delay(TimeSpan.FromSeconds(1.0));
}

And what if F4 need to use the result of the library function? Well that's when ContinueWith comes in to play.

private Task F4() =>
    ThirdParty
        .LibraryFunction()
        .ContinueWith(t =>
        {
            Console.WriteLine("Done.");
        });

Just remember that async/await is just syntactic sugar added on to the language to support TPL.

Upvotes: -2

Related Questions