Quantcast
Channel: Rick Strahl's FoxPro and Web Connection Weblog
Viewing all articles
Browse latest Browse all 133

Calling async/await .NET methods with wwDotnetBridge

$
0
0

I got a question on the message board a week ago regarding calling async .NET methods using wwDotnetBridge. My immediate suspicion was that this probably wouldn't be possible since async code on .NET usually uses generics and requires special setup.

However, as it turns out, you can call async methods in .NET with wwDotnetBridge. In this post I describe how async/await methods work in .NET and how you can call them from FoxPro with wwDotnetBridge.

How async/await works in .NET

The async and await pattern in .NET seems like magic - you make a request to a function that processes asynchronously, which means the code runs in the background and then continue processing the code as if it was running synchronously. async and await code looks and feels just like synchronous code but behind the covers, the code actually runs asynchronously.

.NET does this via some magic with the compiler that effectively re-writes you linear code into a state machine. That generated code essentially creates the dreaded async pyramid of doom that nobody wants to code up, but hides it behind generated compiler code - you never look at the series of continuations.

At a lower level, .NET uses the Task or Task<t> class API which is like a .NET version of a promise. Task is essentially task forwarder, that calls a method asynchronously, then handles the callback and provides a Result property that has a result value (if any). There are options to check completion status as well as methods that can wait for completion. In fact you can just choose to wait on .Result which is a blocking getter that won't return until the result returns.

Task is the low level feature - async and await is language sugar built around the Task object that essentially builds a state machine that waits for completion internally and includes checks and methods that can check the current state of the async request. Methods exist to wait for completion, to continue processing with the result (.ContinueWith() which is what async uses) as well as a .Result property that blocks until the result is available.

In essence, async and await chops up linear code into nested blocks code that continue in a linear fashion. For the developer the beauty of async await is that it looks and behaves mostly like linear code while running asynchronously and freeing up the calling thread.

An example of Async/Await in .NET

Let's say I want to make an HTTP call with System.Net.WebClient which has a number of async methods.

public async Task<string> MakeHttpCall(string url)
{
    var client = new WebClient();
    string http  = await client.DownloadStringTaskAsync(url);
    return http;
}

Remember the magic here - the compiler is doing a bunch of stuff to fix up this code. Note in order for asyncawait to work the method called has to be async to start with, which means the caller has to be calling asynchronously. Async await can be a real rabbit hole as it worms its way up the stack until reach a place where an Async method can be started (usually an event or server generated action). Another way to is to Task.Run() to kick of your own Task to kick of an async operation sequence.

Also note the compiler magic that makes it possible for the method to return Task<string>, but the code actually returning a string. Async methods automatically fix up any result type into a task so the string result becomes Task<string>.

To be clear when we want to call an async method from FoxPro, we can't use this same approach, but there are other ways to retrieve results from Async call in our non-async capable, event less Foxpro environment.

It's also possible to call async methods without using await. As seen above, an async method is really just a method that returns a Task. So WebClient.DownloadStringTaskAsync() - which is an async method that normally is called with asyncawait, can also be called like this:

public string MakeHttpCall(string url)
{
    var client = new WebClient();
    var task = client.DownloadStringTaskAsync(url); // returns immediately
    // waits until Result is available
    string http = task.Result;
    return http;
}

Here the code is directly working with the lower level Task API and it uses the .Result property to wait for completion. If .Result is not ready yet, retrieving .Result blocks and waits for completion of the async task before the value is returned.

This pretty much defeats the purpose of async since we end up waiting for the result, but keep in mind that you do have the option of running other code between the retrieval of the Task and getting the Result property.

This code looks like something that we can call with wwDotnetBridge.

Calling an Async method with wwDotnetBridge

And as it turns out we can in fact call DownloadStringTaskAsync() with FoxPro code just like this:

do wwDotNetBridge
LOCAL loBridge as wwDotNetBridge
loBridge = CreateObject("wwDotNetBridge","V4")

loClient = loBridge.CreateInstance("System.Net.WebClient")

*** execute and returns immediately
loTask = loBridge.InvokeMethod(loClient,"DownloadStringTaskAsync","https://west-wind.com")
? loTask  && object

*** Waits for completion
lcHtml = loBridge.GetProperty(loTask,"Result")
? lcHtml

And this works just fine.

Note that you have to call the async method indirectly with InvokeMethod() and you have to retrieve the Result value from the Task<T>Result using GetProperty(). This is required because both the method and the result property use .NET generics and those can't be called directly through COM interop and require wwDotnetBridge's indirect processing. But it works! Yipiee!

wwDotnetBridge - More that you think!

I was pretty convinced that this wasn't going to work, but in hindsight it makes perfect sense that it does. Async methods are just methods that return a Task object, which can be accessed and manipulated like any other object in .NET and therefore with wwDotnetBridge. The main consideration for wwDotnetBridge is that Task<t> is a generic type and requires indirect access using InvokeMethod() to call the async method, and using GetProperty() to retrieve the Result property.

Be careful

All that said, I'm not sure if it's a great idea to actually do this. Async methods run in the background and potentially on background threads and Microsoft strongly recommends you don't use .Result to wait for completion. They are of the "don't call us we call you!" persuasion, by using ayncawait, or by using Task continuations (ie. .ContinueWith(result) which is something we can't do directly with wwDotnetBridge (can't create delegates).

However, if you are running inside of FoxPro over COM (as we are with wwDotnetBridge) there's already thread marshalling happening that should prevent any thread conflicts from manifesting with async code. Running a few tests firing off 10 simultaneous requests and collecting them seems to work reliably even for long runs. Still make sure you test this out to make sure you don't run into thread lock up or corruption. Check, test and be vigilant if you go down this path.

So there you have it: Task Async methods with wwDotnetBridge are possible. More APIs to connect with for FoxPro. Rock on!

this post created and published with Markdown Monster

Viewing all articles
Browse latest Browse all 133

Trending Articles