KansaiRobot
KansaiRobot

Reputation: 9912

Asynchronous Programming: await inside a function that is not async

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)

Upvotes: 3

Views: 4433

Answers (4)

Wiktor Zychla
Wiktor Zychla

Reputation: 48230

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);
    }
}

Upvotes: 3

Daniel Ormeño
Daniel Ormeño

Reputation: 2778

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.

Upvotes: 0

Johan Donne
Johan Donne

Reputation: 3285

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");
    }

Upvotes: 0

Eike B
Eike B

Reputation: 231

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.

Upvotes: 0

Related Questions