0

What is the purpose of the await keyword in the following code?

Does it create new thread for PostAsync function and then wait until that thread completes to return the await result?

Or does it not wait?

In the code below, would it be possible for Pins.Add function to execute before the firebase.PostAsync call is completed?

    public async Task AddData(int id, string description, DateTime postdate, string usernames)
    {
        await firebase
          .Child("Entries")
          .PostAsync(new Entry() { id = id, description = description, postdate = postdate, username = username });
    }

    public MapPage()
    {
        AddData(1, result, DateTime.Now, deviceId);
        customMap.Pins.Add(new CustomPin
        {
            ..//
        });
    }

I cannot remove the async keyword from definition from AddData, so if this is incorrect, how would I fix this? The other option is to remove both keywords, would this still work with FireBase PostAsync call?

public void AddData(int id, string description, DateTime postdate, string usernames)
{
    firebase
      .Child("Entries")
      .PostAsync(new Entry() { id = id, description = description, postdate = postdate, username = username });
}
Mi Po
  • 1,123
  • 2
  • 12
  • 21
  • You could get rid of the `async` and `await` in `AddData` if you directly return the `Task`, since it's the only statement in the method. You should still `await` the _call_ to `AddData` though. – Bradley Uffner Oct 05 '20 at 00:05
  • 1
    `In the code below, would it be possible for Pins.Add function to execute before the firebase.PostAsync call is completed?` In the code as written, yes. – mjwills Oct 05 '20 at 00:19
  • 1
    I think the question's title could get a bit of editing, but frankly, I have no ideas which sub-question to focus on :/ – quetzalcoatl Oct 05 '20 at 00:24

2 Answers2

1

What is the purpose of the await keyword in the following code?

It conceptually pauses the execution of the current method, until the awaited-thing completes.

Does it create new thread for PostAsync function and then wait until that thread completes to return the await result?

No, await itself does not create any new threads, and it does not really wait. It just looks like it does.

Or does it not wait?

Yes, it does not cause the thread to wait in the 'does it block?' sense. However, execution of the rest of 'AddData' method is paused. Yes, I've said 'AddData'. Execution of 'MapPage' is not affected. That may be a bug in your code (explained below).

In the code below, would it be possible for Pins.Add function to execute before the firebase.PostAsync call is completed?

Yes it certainly is.

I cannot remove the async keyword from definition from AddData, so if this is incorrect, how would I fix this?

AddData needs to be marked as async and needs to return a Task because its implementation uses await keyword. Using the await keyword is not a must. You may handle the Task returned by PostAsync in some other way. But when using await keyword, you MUST be in async-marked-Task-returning method.

If by "fix this" you mean you're concerned that Pins.Add should not execute before PostAsync completes, then you have to propagate the async-await pattern and make the MapPage "wait" for the result of AddData:

public async Task MapPage()
{
    await AddData(1, result, DateTime.Now, deviceId);
    customMap.Pins.Add(new CustomPin
    {
        ..//
    });
}

That's the bare minimum to answer your questions, but I think you may find a few more notes helpful.

As you may have noticed, if you HAVE to await the AddData, then it may mean that the caller of MapPage will have to await the MapPage as well. And so on. Maybe somewhere up above the call stack you will have the freedom to not await or to work around it in some way. But in general, it propagates up the call chain. Like an exception. It's not one of course, but think of it: if your current method has to "wait for something" then the method above.. ..probably has to be able to as well. At least if it wants to know the result of your current method - it has to wait until your current method actually produces that result, right? So it has to propagate as long as the result is important to the caller.

To explain how that happens: await splits the code in half, and POSTPONES the other half.

public async Task AddData(...)
{
    await firebase
      ....
      .PostAsync(...);

    int x = 5;
}

becomes something like:

public async Task AddData(...)
{
    Task tmp = firebase
      ....
      .PostAsync(...);

    Task continued = tmp.ContinueWith( continuation );

    return continued;
}

private .. continuation()
{
    int x = 5;
}

Note how there's no wait/sleep/etc. However, the int x=5 is delayed, until the original task returned from PostAsync notifies everyone that it has just completed.

Note how original task from firebase gets a 'continuation' chained to it, but a different task is returned: the continuation-task.

This means that the await (if you add it there of course) in MapData awaits not for just the firebase task, but for firebasetask+contination.

If there were more awaits in the AddData method, there would be more slices, more chained continuations, but the task that is eventually returned to the caller covers it all.

Now, any awaits in the MapData would do the same: it would split the code, register a continuation, and return the resulting task for someone to observe it.

The caller will get a Task again. But the caller doesn't realy HAVE TO await it. The called may run a thread that will await for the task. The caller may attach a continuation. Or put the task onto some queue and hand it off for someone else to handle. I mean that the async/await look like 'contagious disease' that always propagates upwards, but you can stop at any time it if you really need.

Also, as you already noted, the caller of AddData does not have to await that task, if the caller is not worried about the order of operations further down in the code. If the Pins.Add is allowed to run before firebase task completes, it's fine to not await for AddData. That's why not-awaiting an async method is not a compilation error. You will probably get a warning though, because the AddData returns a Task and that Task is ignored (not awaited, not stored, not chained .ContinueWith etc).

quetzalcoatl
  • 32,194
  • 8
  • 68
  • 107
0

This should work...

public async Task MapPage()
{
    Task t = AddData(1, result, DateTime.Now, deviceId);
    await t;
    customMap.Pins.Add(new CustomPin
    {
        ..//
    });
}

Continuation...


Mi Po follow up question:: would this also work as a solution?
public async Task MapPage() 
{     
   await AddData(1, result, DateTime.Now, deviceId);     
   customMap.Pins.Add(new CustomPin     {         ..//     }); 
}

Yes it would work...but in your particular case your doing an http post and you need (IMO) to check the result (status code) to determine what to do next. Something along these lines...

public async Task MapPage()
{
    HttpResponseMessage result = await AddData(1, result, DateTime.Now, deviceId);
    if (result.IsSuccessStatusCode)
    {
        customMap.Pins.Add(new CustomPin { ... });
    }
    else
    { 
       ...
    }
}

You would also need to change AddData to return the responce

Chris Catignani
  • 5,040
  • 16
  • 42
  • 49