3

To understand the question please take a look on the await calls and the definition of the function InitSyncContext() of the following example.

Based on that i would like to know how the program will behave on each scenario because i don't fully understand what´s the difference between calling await InitSyncContext(store) and having a await call inside without returning a Task.

For reference i did a research before and i found a similar example here however i think it's different in my case.

*The following code is a simplified example from a real world code just for demonstration purposes.

void Main()
{
    Initializer();
}

private async void Initializer()
{
    var store = InitLocalStore();
    await InitSyncContext(store); // <-- Here, await call
    InitFileSync(store);
}

// Here returns Task (without having a return inside. No compile errors)
private async Task InitSyncContext(MobileServiceSQLiteStore store)
{
    await Client.SyncContext.InitializeAsync(store);
}

//------------- Second Method -------------

void Main()
{
    Initializer();
}

private void Initializer()
{
    var store = InitLocalStore();
    InitSyncContext(store); // <-- Here without await call
    InitFileSync(store);
}

// Here is void but with a async call inside
private async void InitSyncContext(MobileServiceSQLiteStore store)
{
    await Client.SyncContext.InitializeAsync(store);
}
CodeArtist
  • 5,534
  • 8
  • 40
  • 65
  • 1
    You can't use the await keyword inside a non-async method. It's not valid grammar in the C# language. This doesn't compile – Steve Cooper Jul 14 '16 at 07:49
  • @SteveCooper thanks for point it out. I corrected the example. This code is taken from a real world code (but without the Main definition) so i can test it. This way compiles. – CodeArtist Jul 14 '16 at 07:52
  • 2
    _"[Prefer async Task methods over async void methods](https://msdn.microsoft.com/en-us/magazine/jj991977.aspx?f=255&MSPPError=-2147217396)"_ –  Jul 14 '16 at 07:54
  • @MickyD - stick this as an answer. I'll upvote... – Steve Cooper Jul 14 '16 at 08:15
  • @SteveCooper Done. Thanks :) –  Jul 14 '16 at 08:25
  • @CodeArtist rather than simply posting so link only answers, I actually explained it here. I hope this clears things up, thank you for taking the time to ask this on SO. – David Pine Jul 14 '16 at 10:53
  • @CodeArtist Your problem here is with the mobile app's `App.App()` constructor, am I right? Not `Main()` as in your example? If so, does it work the same as for a console app? – h bob Jul 15 '16 at 17:59
  • 1
    @hbob actually yes that was the problem but my question was specifically on how will behave `await` in a similar environment (not necessarily using Xamarin but C#). To use the `await` in class constructor method i implemented the factory pattern described here http://blog.stephencleary.com/2013/01/async-oop-2-constructors.html and i haven't tried to use Xamarin in console apps. – CodeArtist Jul 15 '16 at 21:05
  • @CodeArtist Yep that works very well, thanks! – h bob Jul 16 '16 at 07:33

3 Answers3

4

What's the difference between awaiting async Task function and calling await inside a void function?

This is perhaps a very big question is best served by reading the fine article Async/Await - Best Practices in Asynchronous Programming by Mr Stephen Cleary.

Some summary points:

Avoid async void - Prefer async Task methods over async void methods

enter image description here

See also

3

What's the difference between awaiting async Task function and calling await inside a void function?

The whole world! Everything...and then some. Let's take a step back and start with some of the basics.

You should go "async all the way", i.e.; if you're returning an awaitable (Task or Task<T>) these should correctly use the async and await keywords.

There are several things that need to be clarified here. You should not have async void methods, instead you should have async Task - the exception being event handlers (where the delegates are predefined as non-task returning) and the like. Let's examine and pick apart method one (I'm going to ignore the Main entry points):

One

private async void Initializer()
{
    var store = InitLocalStore();
    await InitSyncContext(store); // <-- Here, await call
    ...
}

// Here returns Task (without having a return inside. No compile errors)
private async Task InitSyncContext(MobileServiceSQLiteStore store)
{
    await Client.SyncContext.InitializeAsync(store);
}

You have an Initializer method that is immediately a code smell as it is async void. This should be async Task and it should have the "Async" suffix. Additionally, you have an InitSyncContext that takes on a store variable and invokes some client initialization work. The code smell here is that you are using async and await. This is not need on simple (single task) workloads like this. Instead you should simply use the return keyword. Example at the very bottom. Let's look at method two:

Two

private void Initializer()
{
    var store = InitLocalStore();
    InitSyncContext(store); // <-- Here without await call
    ...
}

// Here is void but with a async call inside
private async void InitSyncContext(MobileServiceSQLiteStore store)
{
    await Client.SyncContext.InitializeAsync(store);
}

Things have officially gone from bad to worse! With the misconceptions of the asynchronous nomenclatures, we have assumed that since one method appeared to work "ok" without taking into account the best practices that another method could follow suit. You made the InitSyncContext method async void. The reason methods should not be async void is that they are fire-and-forget. The internal async state machine doesn't have a Task to latch onto and therefore the state is lost. When you remove the caller's await you are saying start this asynchronous operation but you do not care about it's results.

Here is the correct way to implement the desired functionality:

Correct

private async Task InitializerAsync()
{
    var store = InitLocalStore();
    await InitSyncContextAsync(store);
    ...
}

// Simply return the task that represents the async operation and let the caller await it
private Task InitSyncContextAsync(MobileServiceSQLiteStore store)
{
    return Client.SyncContext.InitializeAsync(store);
}

A note about the Main method from your snippets, if this is part of a console application - the top-level entry point into your async stack would invoke either .Result or .Wait().

Further reading

David Pine
  • 23,787
  • 10
  • 79
  • 107
  • When you said "top-level entry point into your async stack would invoke either .Result or .Wait()" do you mean that one needs to do something special in `Main()`? What would that be? If we go "async all the way", how do you deal with the fact that `Main` is not async? – h bob Jul 15 '16 at 17:08
  • 1
    @hbob, yes. since `Main` is invoking an `async` method chain, we need to ensure that we call `.Wait()` on it. Of course assuming that this is a console application. – David Pine Jul 15 '16 at 17:17
  • Oh you mean inside the console's `Main` function (as in OP's example) we would do `InitializerAsync().Wait()` and that would make it "async all the way"? (That would be blocking though, but I guess that makes sense so the app doesnt exit.) – h bob Jul 15 '16 at 17:38
  • 1
    @hbob Right, in console applications there is no native support for `async` at the entry point of the application. Think about it, if you're on the command line and attempting to invoke the `.exe` it doesn't know what a `Task` is. See [here](http://stackoverflow.com/a/9212343/2410379) and [here](http://stackoverflow.com/a/17630538/2410379) for more details. – David Pine Jul 15 '16 at 17:55
-1

In first example compiler generate something like this:

TheadPool.RunTask(()=>InitSyncContext(store)).ContinueWith(()=>InitFileSync(store))

At second — nothing interesting. Because nobody wait the end of the task. All calls will be in main thread exept await Client.SyncContext.InitializeAsync(store);

Arheus
  • 172
  • 7