3

First the question: Can I use await inside a function that is not marked as async??

Now the details. I was reading this post Hololens- Capturing Photo... and as you can see the author posted some code. Among it this

 void Start ()
    {
        getFolderPath();
        while (!haveFolderPath)
        {
            Debug.Log("Waiting for folder path...");
        }
        Debug.Log("About to call CreateAsync");
        PhotoCapture.CreateAsync(false, OnPhotoCaptureCreated);
        Debug.Log("Called CreateAsync");
    }


    async void getFolderPath()
    {
        StorageLibrary myPictures = await Windows.Storage.StorageLibrary.GetLibraryAsync(Windows.Storage.KnownLibraryId.Pictures);
        Windows.Storage.StorageFolder savePicturesFolder = myPictures.SaveFolder;
        Debug.Log("savePicturesFolder.Path is " + savePicturesFolder.Path);
        folderPath = savePicturesFolder.Path;
        haveFolderPath = true;
    }

Now notice how the getFolderPath returns void (as an event handler) but the documentation says that these methods can't be awaited. The author instead await by using a while loop.

But what if I do this

     void Start ()
        {
            await getFolderPath();

            Debug.Log("About to call CreateAsync");
            PhotoCapture.CreateAsync(false, OnPhotoCaptureCreated);
            Debug.Log("Called CreateAsync");
        }


     //Notice how now it returns Task
        async Task getFolderPath()
        {
            StorageLibrary myPictures = await Windows.Storage.StorageLibrary.GetLibraryAsync(Windows.Storage.KnownLibraryId.Pictures);
//.....
        }

Can I do this? (notice that Start() is not Async)

4 Answers 4

3

People tend to forget what's behind async and await.

No, you can't await in a method that's not async but you can call ContinueWith on the returned Task and provide explicit continuation that executes only when the task is complete:

class Example
{
    public void Start()
    {
        getFolderPath()
           .ContinueWith(t =>
           {                   
               Console.WriteLine("...");
           });
    }

    async Task getFolderPath()
    {
        await Task.Delay(1000);
    }
}

That's equivalent to

class Example
{
    public async Task Start()
    {
        await getFolderPath();
        Console.WriteLine("...");
    }


    async Task getFolderPath()
    {
        await Task.Delay(1000);
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

Technically true, but ContinueWith is a low-level, dangerous API. You can (and should) use await instead.
@StephenCleary: while I agree it's "low-level", I scratch my head over the "dangerous". Is it just an opinion or there is some rationale behind?
Here's a lengthy discussion. In summary, the most common dangerous parts are: it doesn't understand async delegates, it doesn't use the thread pool by default (as most people assume), its CancellationToken will cancel a running delegate. It also has default values for its arguments that only make sense if you're doing dynamic task parallelism, not asynchronous programming; in particular, you should always pass a TaskScheduler and TaskContinuationOptions when using it for async programming.
0

You can not await function calls without marking the method as async. So your second example will not compile. But you can mark the Start method as async.

Async await is a non blocking method to wait for execution of functions. In this case, the execution will stop at the await in the GetFolderPath method, but while this will wait, the execution in the Start method will continue. I assume, that because of this, the author uses the while loop to wait for the execution of getFolderPath to finish.

Comments

0

You could explicitly wait on the GetFolderPath method to finish:

    void Start ()
    {
        getFolderPath().Wait();

        Debug.Log("About to call CreateAsync");
        PhotoCapture.CreateAsync(false, OnPhotoCaptureCreated);
        Debug.Log("Called CreateAsync");
    }


 //Notice how now it returns Task
    async Task getFolderPath()
    {
        StorageLibrary myPictures = await Windows.Storage.StorageLibrary.GetLibraryAsync(Windows.Storage.KnownLibraryId.Pictures);
        //.....
    }

But then you are turning the asynchronous method in a synchronous one, since Wait is a blocking method (but that is what happens in the while loop anyway).

If your aim is to keep the operation asynchronous, you have to go with Daniel Ormeño's suggestion:

    async Task Start ()
    {
        await getFolderPath();

        Debug.Log("About to call CreateAsync");
        PhotoCapture.CreateAsync(false, OnPhotoCaptureCreated);
        Debug.Log("Called CreateAsync");
    }

3 Comments

That is what puzzles me about asynchronous programming. I mean once you are done with things to do unrelated to the called async function, and all you have to do is wait, that becomes synchronous, no?
You have to wait in any case. The question is whether the thread you are running in is blocked or not. Asynchronous: your code stops executing until the await returns, but the thread can still handle other actions. Synchronous: the whole thread is blocked waiting. This is most relevant if the thread your code is running in is the UI thread. If you use an asycnhronous call, the UI remains responsive, if you wait blocking, it freezes.
@Daniel Ormeño: I know (as was indicated in my answer). The technique can be usefull if you need to consume an async method you cannot change (e.g. in a 3rd part class library) from a method you must (or want to) keep synchronous.
0

The answer to your first question is No, you can't. From the official documentation

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.

If you want to await the execution of the async method getFolderPath inside the start method, you need to update the signature to be

public async Task Start ()
{
    await getFolderPath();

    Debug.Log("About to call CreateAsync");
    PhotoCapture.CreateAsync(false, OnPhotoCaptureCreated);
    Debug.Log("Called CreateAsync");
}

In the async await context, Task as a return type, means that the task does not return a value, which is equivalent to void in the synchronous approach. if for example, you needed to return an string from an asynchronous task, you would need to have public async Task<string> GetFoo().

Overall, I think the code you from the example you are looking at needs some reviewing.

5 Comments

Thanks. However, isn't async and not Task there? (notice how getFolderPath returns Task in my code above. I think I understand the equivalency you talk about. But if Start is not async how can it return Task?
@KansaiRobot: indeed, it should be 'public async Task Start()'.
Yes thank your for pointing it out, I forgot to add include it
@KansaiRobot you can return Task even if a method is not async. All async does is enable await (which generates a state machine for continuations) and handles exceptions correctly. Basically, if you want to use async, use async all the way up and down.
@KansaiRobot yes, keep in mind that async is just a keyword, its not the return type of the method. When labeling the method as async, the method must return a Task<T> where T is the return type. If you do not await the method call, the method will then return a regular Task.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.