0

I have this code that's run after a button is pressed:

    partial void DownloadPressed(NSObject sender)
    {
        Console.WriteLine("Pressed");

        BeginInvokeOnMainThread(() => {
            Label1.StringValue = "Fetching Data";
            Label2.StringValue = "Fetching Data";
            Label3.StringValue = "Fetching Data";
        );
        DownloadFromAzure();
        Label1.StringValue = "Phrases: " + psNet.Count;
        Label2.StringValue = "CategorySource: " + csNet.Count;
        Label3.StringValue = "CategoryGroupSource: " + cgsDb;
    } 

I've tried quite a few different things but I cannot get it to display the Fetching Data messages. I assume this is because it's all running in the same thread with no waiting.

Can anyone give me advice on how I could run the method with an await.

    private static void DownloadFromAzure()
    {
        // Some database and HTTP calls here

    }
Alan2
  • 23,493
  • 79
  • 256
  • 450
  • what does DownloadFromAzure do? Can you make it async? If it's primarily doing HTTP requests, those should be async calls. – Jason Feb 06 '19 at 15:15
  • Making a method async doesn´t make it use multiple threads. You can even run an async method on the same thread. – MakePeaceGreatAgain Feb 06 '19 at 15:16
  • Do you have an opportunity to edit asynchronous operations that are inside the `DownloadFromAzure` function to make them synchronous or at least change signature and use everywhere async/await? – mexanichp Feb 06 '19 at 15:17
  • I can change anything in DownloadFromAzure but my problem might be that the method it's called from is not async. Seems like there are not many examples of how to do this with xamarin.mac or at least not many I have been able to find yet? – Alan2 Feb 06 '19 at 15:32

3 Answers3

4

uh.. no?

await requires the argument to be awaitable.. see https://blogs.msdn.microsoft.com/pfxteam/2011/01/13/await-anything/ how to make your own awaitables for things that does not return Task<>...

but I'm not sure if it is really worth the effort. It still requires you to be able to track the start/progress/stop/error conditions of the pending job, anc considering you have just a single linear function call, you probably dont have any such information at hand.

It'd be better to refactor/redesign the DownloadFromAzure to actually return a Task that you can await easily.

EDIT:

in dire cases, you can always wrap it with Task.Run

partial async Task DownloadPressed(NSObject sender)
{
    Console.WriteLine("Pressed");

    BeginInvokeOnMainThread(() => {
        Label1.StringValue = "Fetching Data";
        Label2.StringValue = "Fetching Data";
        Label3.StringValue = "Fetching Data";
    );

    await Task.Run( () => DownloadFromAzure() );   // <-----HERE

    Label1.StringValue = "Phrases: " + psNet.Count;
    Label2.StringValue = "CategorySource: " + csNet.Count;
    Label3.StringValue = "CategoryGroupSource: " + cgsDb;
} 
quetzalcoatl
  • 32,194
  • 8
  • 68
  • 107
  • Does await of that function really solve the problem? I think it will be just executed in a different thread but since `DownloadFromAzure` has asynchronous invocations inside this will not help achieve the goal. What do you think? – mexanichp Feb 06 '19 at 15:23
  • When I try making this change I get an error saying Error CS4033: The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'. – Alan2 Feb 06 '19 at 15:28
  • @mexanich: and what was the goal in the first place, a goal which isn't helped? I don't see any not adressed. OP asked about awaiting on that, and we have 2 options: either downloadfromazure does it asynch. way but immediately waits for completion and blocks, or it is not asynch. at all and simply blocks. If we're talking about 'awaiting' for that, then the only goal is to remove the 'blocking' from the thread that right now calls DownloadFromAzure. Task.Run and rewriting the method into async/await solves that - the current thread is released and some thread continues it later.. – quetzalcoatl Feb 06 '19 at 23:21
  • @Alan2 ^ corrected now, sorry, I was in a hurry and I assumed you'll know the total basics since you're already asking about 'awaiting' for things and I didn't pay attention enough to notice that you're lacking 'async' in function signature.. – quetzalcoatl Feb 06 '19 at 23:22
0

If I understand you correctly, you want to turn the synchronous call to DownloadFromAzure() to an awaitable call.

There are multiple ways to do this, including modification of DownloadFromAzure() to an async method.

But if this is not the best approach (which may be the case), you can simply wrap it into a Task:

await Task.Run(() => DownloadFromAzure());

Update, due to comment:

partial void DownloadPressed(NSObject sender)
{
    Console.WriteLine("Pressed");

    DoDownloadAsync();
}

private async void DoDownloadAsync()
{
    BeginInvokeOnMainThread(() => {
        Label1.StringValue = "Fetching Data";
        Label2.StringValue = "Fetching Data";
        Label3.StringValue = "Fetching Data";
    );

    await Task.Run(() => DownloadFromAzure());

    Label1.StringValue = "Phrases: " + psNet.Count;
    Label2.StringValue = "CategorySource: " + csNet.Count;
    Label3.StringValue = "CategoryGroupSource: " + cgsDb;
}

Note that this is a "fire and forget" approach that may only be used in this form, if DoDownloadAsync() makes proper error handling on its own responsibility (which may be included in DownloadFromAzure() here) and the caller (DownloadPressed here) does not care of tracking the Task's progress! In any other case, DoDownloadAsync() should return the Task that it is awaiting, for the caller being able to track.

See e. g. here for more in depth discussions: async/await - when to return a Task vs void?

Nicolas
  • 754
  • 8
  • 22
  • The problem with this is that the method that calls DownloadFromAzure is a partial method and not async. It's a method that's fired when a button is pressed. – Alan2 Feb 06 '19 at 15:30
  • @Alan2 well, than you have to wrap everything inside an async method ... I will update my answer – Nicolas Feb 06 '19 at 15:53
  • Worked very well. Thank you – Alan2 Feb 06 '19 at 16:17
  • @quetzalcoatl Generally I don't like approaches like "this pattern was designed for concrete xy usage, so avoid it whenever else". IMHO each solution may be chosen, if it fits the requirements. Of course one has to be aware of the implications and this is what you're talking about, I assume! The lack of ability to track a `Task`, if no Task is returned is obvious in my eyes. ;-) Discussing use cases for the `async void` pattern is not black/white, as we can see here: https://stackoverflow.com/questions/12144077/async-await-when-to-return-a-task-vs-void – Nicolas Feb 07 '19 at 07:34
  • @quetzalcoatl yes you're right, it is important to explain the implications, I updated my answer – Nicolas Feb 07 '19 at 07:57
  • thanks! another article if you'd like to read about it more - https://haacked.com/archive/2014/11/11/async-void-methods/ - aand I removed my comments since they are of no use now :) – quetzalcoatl Feb 07 '19 at 08:07
0

Answer is NO

In official documentation for await keyword:

The await operator is applied to a task in an asynchronous method to insert a suspension point in the execution of the method until the awaited task completes. The task represents ongoing work.

await can only be used in an asynchronous method modified by the async keyword. Such a method, defined by using the async modifier and usually containing one or more await expressions, is referred to as an async method.

So, your options are:

  1. Make DownloadFromAzure() as asnyc
  2. add following piece of code:

    await Task.Run(() => DownloadFromAzure())

Djordje Nedovic
  • 559
  • 8
  • 20
  • But this would require me also to make the DownloadPressed async would it not? – Alan2 Feb 06 '19 at 15:40
  • Look at this... maybe will be easier to understand https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.wait?view=netframework-4.7.2#System_Threading_Tasks_Task_Wait – Djordje Nedovic Feb 06 '19 at 18:09