The new async / await syntax in .NET 4.5 + makes asynchronous programming really easy. However, there are times when having an assumption of asynchrony can impede you. One such example is programming for games (see my post on why you might want to avoid this).
However, what happens when you want to display a windows message box, or some other action that is asynchronous; See my post on a message box helper for an example.
In my particular case, I was to show a message box asynchronously, and perform a certain action based on the result; however, I don’t want to stop the game, and I don’t want to have to introduce an async / await into the programming model (for reasons in the linked post).
My solution was to use a combination of two, slightly outdated, methods of asynchronous programming: call backs and continuation blocks (strictly speaking, async / await does use continuation blocks behind the scenes admittedly). The following code will attempt to make an in-app purchase from the store:
internal static async Task<bool> PurchaseCash()
{
var result = await Windows.ApplicationModel.Store.CurrentApp.RequestProductPurchaseAsync(PURCHASE);
return (result.Status == ProductPurchaseStatus.Succeeded);
}
What that function actually does it not important; however, it needs to be called from within a game loop. Here’s how it is called:
Purchase.PurchaseCash().ContinueWith((purchaseTask) =>
{
purchaseTask.Wait();
if (purchaseTask.Result)
{
App.settings.CashPot.Total += Purchases.Purchase.MORE\_CASH\_AMOUNT;
}
});
This will only execute the purchase action if the purchase was successful; it’s completely asynchronous, and it doesn’t affect the main thread. All well and good, but what if, instead of a specific task, we wanted to execute a conditional command; for example: when the purchase is called, we want to turn on a specific feature.
In this case, I decided to use a call back; the method signature looks like this:
private bool MakePurchase(int cost, Action onSuccess)
And it is called like this:
if (!App.settings.Purchase1)
{
MakePurchase(PURCHASE1\_COST, () =>
{
App.settings.Feature1 = true;
});
}
Inside MakePurchase, I only call the onSuccess method where the purchase was successful:
Purchase.PurchaseCash().ContinueWith((purchaseTask) =>
{
purchaseTask.Wait();
if (purchaseTask.Result)
{
onSuccess.Invoke();
}
}
Conclusion
The syntax above is nowhere near as clear and concise as a simple await statement; however, await statements can’t be used outside of an async method and, especially when programming games, that’s not always practical. The other thing that I haven’t mentioned here is exception handling - I may make that the subject of a later post.