-1

I have the following code and I want that it is executed in the correct order, but I'm not sure if I use "await" correctly so that it is executed in the correct order.

The correct sequence should be:

1)Call GetTiltleData and get the CurrentCatalogVersion.

2)Call GetCatalogData to get the ItemID of the item that will get purchased.

3)Call MakePurchase to purchase the item.

4)Call GetInventoryList to get the player's current(after the purchase) inventory.

Am I using await correctly? Is my code executed in the correct order or is it possible that the code could be executed in a wrong order?

For example, is it possible that the code of GetCatalogData(); is executed before CurrentCatalogVersion = result.Result.Data["Catalogversion"]; ?

string CurrentCatalogVersion = "";
string ItemID = "";
int CurrentAmount = 0;

GetTiltleData();
GetCatalogData();

public async void GetTiltleData()
{
    await ClientGetTitleData();
}

private async Task ClientGetTitleData()
{
    var result = await PlayFabClientAPI.GetTitleDataAsync(new GetTitleDataRequest());

    if (result.Result.Data == null || !result.Result.Data.ContainsKey("Catalogversion"))
        Console.WriteLine(result.Error.GenerateErrorReport());
    else
        CurrentCatalogVersion = result.Result.Data["Catalogversion"];
}

public async void GetCatalogData()
{
    await GetCatalog();
}

private async Task GetCatalog()
{
    var result = await PlayFabClientAPI.GetCatalogItemsAsync(new GetCatalogItemsRequest()
    {
        CatalogVersion = CurrentCatalogVersion
    });

    foreach (var entry in result.Result.Catalog)
    {
        //For example, if you want to purchase a sword
        if (entry.DisplayName == "Sword")
            ItemID = entry.ItemId;
    }

    if (result.Error != null)
    {
        Console.WriteLine(result.Error.GenerateErrorReport());
    }
    else
    {
        Console.WriteLine("Listed items successful!");
        await MakePurchase(ItemID);
    }
}


private async Task MakePurchase(string itemid)
{
    var result = await PlayFabClientAPI.PurchaseItemAsync(new PurchaseItemRequest()
    {
        CatalogVersion = CurrentCatalogVersion,
        ItemId = ItemID,
        Price = 100,
        VirtualCurrency = "GO"
    });

    if (result.Error != null)
    {
        Console.WriteLine(result.Error.GenerateErrorReport());
    }
    else
    {
        Console.WriteLine("Purchase successful!");
        await GetInventoryList();
    }
}

private async Task GetInventoryList()
{
    var result = await PlayFabClientAPI.GetUserInventoryAsync(new GetUserInventoryRequest());
    //Get the current amount of the player's virtual currency after the purchase
    CurrentAmount = result.Result.VirtualCurrency["GO"];

    foreach (var entry in result.Result.Inventory)
    {
        //Get a list with the player's items after the purchase
        Console.WriteLine($"{entry.DisplayName} {entry.UnitPrice} {entry.ItemId} {entry.ItemInstanceId}");
      ...
    }

    if (result.Error != null)
    {
        // Handle error if any
        Console.WriteLine(result.Error.GenerateErrorReport());
    }
    else
    {
        Console.WriteLine("Got current inventory");
    }
}
John S.
  • 15
  • 4
  • Side notes: 1) Why do you have a method called `Get*` if it doesn't return any data? 2) You might want to rename all methods that are async to end with the word "Async", like `GetTiltleDataAsync`, so you don't forget to await it. 3) Avoid using `async void` in a method signature - use `async Task` instead. – gunr2171 Jun 20 '19 at 15:41
  • 1. If you are going to use `async`/`await` you need to do that in the entire call stack. 2. All methods (with the exception of event handlers like in winforms / wpf) should return have a return type of `Task` or `Task` if they use await/async. – Igor Jun 20 '19 at 15:46

1 Answers1

0

For example, is it possible that the code of GetCatalogData(); is executed before CurrentCatalogVersion = result.Result.Data["Catalogversion"]; ?

Yes, absolutely.

This is your calling code:

...
GetTiltleData();
GetCatalogData();

These are asynchronous methods, but you're not awaiting their results before continuing to the next instruction. This means both methods are fire and forget threads.

This means you have a race condition. Literally, you have 2 threads racing to finish their methods before the other. You have no guarantee which one will finish first, or how the CPU will prioritise their instructions. Because of how you've called those methods, you've essentially told the computer you don't care about the outcome of events.

Fastest way to ensure one method completes before the next one starts is to await them both. As Igor says, if you are going to use async methods, you should use async/await the entire way up and down your call stack.

string CurrentCatalogVersion = "";
string ItemID = "";
int CurrentAmount = 0;

await GetTiltleData(); // adding "await" to these 2 lines
await GetCatalogData();

In addition, PLEASE don't use async void in your method signatures. You should use async Task instead. See async/await - when to return a Task vs void?.

gunr2171
  • 16,104
  • 25
  • 61
  • 88
  • What do you mean with "use async/await the entire way up and down your call stack"? How should my code look like? – John S. Jun 20 '19 at 16:10
  • The method that calls an async method should be async itself. – gunr2171 Jun 20 '19 at 16:11
  • Is this code not correct? Both methods use the word "async". Or is that not what you mean? public async Task GetTiltleDataAsync() { await ClientGetTitleDataAsync(); } private async Task ClientGetTitleDataAsync() { ... } – John S. Jun 20 '19 at 16:35
  • You're right that you're using the `await` keyword _inside_ of the `GetTiltleData` method. But the code _that calls_ the `GetTiltleData` does not use `await`. – gunr2171 Jun 20 '19 at 16:36
  • Ok. So the following code would be correct or not? GetTiltleDataAndGetCatalogDataAsync(); public async Task GetTiltleDataAndGetCatalogDataAsync() { await ClientGetTitleDataAsync(); await GetCatalogAsync(); } – John S. Jun 20 '19 at 16:49