1

As far as I understand, when you want to wait until async method is completed, you have to use await. Otherwise, it's thread will be detached and you'll never hear back from it again. But when you use await, you then have to make the enclosing method async as well (Why though?). That means, you have to use async-await all the way up the call stack. That also means, you can't await-async in constructors.

async Task<int> Boo()
{
    return 1;
}

async Task Far()
{
   int res = await Boo();
}

Is there a way to stop this infestation? Meaning, how to execute Boo synchronously and get the result, without having to make Far async ?

EDIT:

Does this answer your question? How to cancel a Task in await?

No, I need to get task done, not cancel it.

That also means, you can't await-async in constructors. - why would you want to?

I really don't, but it looks like I have to.

Task.Result is only the answer if you like deadlocks. Why are you trying so hard to avoid async/await?

  1. the project is huge
  2. for some external methods there are no reason to run them async, though they declared as such
  3. using results of async operations in constructors

"you then have to make the enclosing method async as well" - no. you can wait for the result synchronously by accessing Task.Result

Using Task.Result looks like the answer, but right now I'm unable to compile with it. Supposedly, RunSynchronously() should also be able to solve problem?

because you don't want half-baked objects if you can help it.

Sure, I don't. Ideally, I want to synchronously get the result of external method, marked async and finish object initialization.

Don't do work in constructors

Good point, but I just can't help myself.

RESOLUTION:

Seems like .GetAwaiter().GetResult(); was just enough to do the trick for me. I would consider this an answer, if the question was not marked duplicate.

George
  • 67
  • 10
  • You are correct, you use async/await all through the callstack. However, you do NOT need to use async/await for CPU-bound methods. Just use normal return types in those cases. As for why, it is because it has to schedule the continuation through a state machine. If you didn't enclose the awaited call, it would be "fire-and-forget" and you wouldn't be able to pick up the continuation. – David L Jan 12 '21 at 19:33
  • Also see: [How and when to use ‘async’ and ‘await’](https://stackoverflow.com/questions/14455293/how-and-when-to-use-async-and-await) – gunr2171 Jan 12 '21 at 19:35
  • Another: [Avoid async from spreading in method signatures](https://stackoverflow.com/questions/44270633/avoid-async-from-spreading-in-method-signatures) – greenjaed Jan 12 '21 at 19:38
  • No, I need to get task done, not cancel it. – George Jan 12 '21 at 19:40
  • _"you then have to make the enclosing method async as well"_ - no. you can wait for the result synchronously by accessing [Task.Result](https://learn.microsoft.com/en-gb/dotnet/api/system.threading.tasks.task-1.result?view=net-5.0) – Franz Gleichmann Jan 12 '21 at 19:40
  • 3
    *That also means, you can't await-async in constructors.* - why would you want to? – Caius Jard Jan 12 '21 at 19:41
  • 1
    Concerning using async..await in constructors, there is an excellent blog bij Stephen Cleary: https://blog.stephencleary.com/2013/01/async-oop-2-constructors.html – Johan Donne Jan 12 '21 at 19:43
  • 2
    @FranzGleichmann, [Don't Block on Async Code](https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html) – Fabio Jan 12 '21 at 19:44
  • 1
    @FranzGleichmann: waiting for the result of an async method using Task.Result is not advisable: it will lead to a deadlock if the async code tries to return to the calling thread!! – Johan Donne Jan 12 '21 at 19:45
  • Using `Task.Result` looks like the answer, but right now I'm unable to compile with it. Supposedly, `RunSynchronously()` should also be able to solve problem. – George Jan 12 '21 at 19:46
  • By the way, "Boo does not use await, and will run syncronously". You don't have to await in Far either, if you can elide it.. but read up on the pitfalls. Right now your question seems more like a rant that you want use TAP but you don't want to use TAP.. – Caius Jard Jan 12 '21 at 19:47
  • *"why would you want to? "* I really don't, but it looked like i have to. – George Jan 12 '21 at 19:48
  • @Yury Task.Result is only the answer if you like deadlocks. Why are you trying so hard to avoid async/await? – mason Jan 12 '21 at 19:48
  • @CaiusJard because you don't want half-baked objects if you can help it. This is a real problem. You can cover it up, eg using factory methods, but this still requires extra code and failsafes – Panagiotis Kanavos Jan 12 '21 at 19:55
  • @JohanDonne i _know_ it is not _advisable_. what i was trying to show was only this: it is _possible_, and therefore it is _not neccessary_ to use await. – Franz Gleichmann Jan 12 '21 at 19:58
  • 2
    @Yury I'll agree that this is just a rant, not an actual question. It's *awaiting* that forces you to go async all the way to the top, not the `await` keyword. Awaiting means the code will *continue* after the async operation completes. `async` tells the compiler to add the continuation machinery. Without it, you don't have an asynchronous constructor. Which, btw is not a trivial job - there's no object before the constructor completes, so who's going to run the continuation? Async constructors are coming, but they aren't here yet – Panagiotis Kanavos Jan 12 '21 at 20:00
  • @FranzGleichmann Just because something is technically possible, doesn't mean you should go mentioning it. It will be taken as advice, whether you meant it as advice or not. If you're going to mention it, you're going to want to be explicit about mentioning why it's not a good idea. Sure one can light a fire using gasoline....but are you just going to say "use gasoline" or would you instead say "gasoline is possible to use, but extremely dangerous and you're likely to burn yourself and start an uncontrollable fire. Use lighter fluid instead of gasoline if at all possible"? – mason Jan 12 '21 at 20:01
  • Don't do work in constructors. – StackOverthrow Jan 12 '21 at 20:01
  • 2
    @Yury you can use factory methods instead of async constructors. Yes, it's extra work, but it works. Your "arguments" for not using `async` aren't real arguments at all. YOU CAN'T write code that isn't async to the top, no matter the technology. What if you used callbacks? All your code would have to use continuation callbacks, all the way to the top. What about events? Same thing – Panagiotis Kanavos Jan 12 '21 at 20:01
  • `RunSynchronously() should also be able to solve problem.` you don't need that at all. – Panagiotis Kanavos Jan 12 '21 at 20:03
  • 2
    Post some *actual* code. What you posted up to now are contrived examples to prove that something doesn't work. Well, all .NET Core applications, all Blazor apps, all new Apis are async-first, so clearly things *do* work. If you have specific issues, people can help you solve them – Panagiotis Kanavos Jan 12 '21 at 20:05
  • 1
    Task.GetAwaiter().GetResult() is a better option than Task.Result. – jalepi Jan 12 '21 at 20:09
  • I think @Yury you need to ask a question that demonstrates an actual problem that needs solving - SO is about questions that can be answered rather than just discussed. Present a concrete example of how you're being forced to await and you don't want to, so we can help with it and consider the ramifications – Caius Jard Jan 12 '21 at 20:10

1 Answers1

3

I think it's a better to approach to use a static method that plays the role of a factory that create instances of your class in an async fashion using a private constructor:

Let me illustrate that using some code

public class MyWorker       
{       
    public IList<TData> Data { get; set; }       

    //static async method that behave like a constructor       
    async public static Task<MyWorker> CreateAsync()  
    {       
        var data= await FetchDataAsync();  
        return new MyWorker(data);
    }       

    // private constructor called by the async method
    private MyWorker (IList<TData> data)
    {
        this.Data = data;   
    }
} 

//..
var worker = await MyWorker.CreateAsync();

this way is less error-prone since you instantiate your class in one line of code (LOC) and you don't need to do that using 2 LOC (one for the instance, one for calling the async method which you or someone else might forget to call).

Benzara Tahar
  • 2,058
  • 1
  • 17
  • 21