Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

await asynchronous method where completion is signalled by event handler

I am working with the Contacts object in Windows Phone 8, calling SearchAysnc from within an async method. SearchAsync requires that a handler be subscribed to the SearchCompleted event, and delivers its results via one of the event args, which the async method requires to do its job (which includes invoking other async methods).

How do you await the asynchronous completion of an event i.e. bridge between the event pattern and the async/await pattern?

The only solution I could come up with was to use an EventWaitHandle, waiting on it within an awaited Task something like this:

using System.Threading;


async Task<string> MyMethod()
{
    string result = null;
    Contacts cons = new Contacts();
    EventWaitHandle handle = new EventWaitHandle(false,EventResetMode.ManualReset);
    cons.SearchCompleted += (sender,args) =>
    {
        // I do all my work on the results of the contact search in this handler
        result = SomeOtherSynchronousOperation(args.Results);

        // When I'm done, I release the thread which was waiting for the results
        handle.Set();
    };
    cons.SearchAsync(String.Empty, FilterKind.None, "My Contact");

    // I can't block this thread (or can I?)
    // So, I launch a task whose sole job is to wait
    await Task.Run(()=>
    {
       // This gets released by the Contacts.SearchCompleted handler when its work is finished,
       // so that MyMethod can finish up and deliver its result
       handle.WaitOne();
    }

    await DoOtherStuffWithResult(result);

    return result;
}

My actual solution (not exactly as shown above) does work. Although the above code doesn't precisely represent the implemented solution, (likely a compile issue or two), it should serve to express the concept and illustrate the point of my question.

It leaves me wondering if this is the only way, or anywhere close to the best practise way to await the execution of an event handler, and if not, what would be the "best practice" to do what is needed here.

Do the Windows synchronization primitives still have a place in an async/await world?

(Based on answers provided)

Would this be correct?

using Microsoft.Phone.UserData;

string ExtractWhatIWantFromResults(IEnumerable<Contact> results)
{
    string result;

    // Do my processing on the list of contacts, stuff the results into result

    return string;
}

async Task<string> MyMethod()
{
    Contacts cons = new Contacts();
    TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();

    cons.SearchCompleted += (sender,args) =>
    {
        tcs.TrySetResult(ExtractWhatIWantFromResults(args.Results));
    };
    cons.SearchAsync(String.Empty, FilterKind.None, "My Contact");

    return tcs.Task;
}
like image 596
Zenilogix Avatar asked Jan 12 '23 06:01

Zenilogix


1 Answers

TaskCompletionSource is the common way to use.

Not tested (No idea how to test without knowing your classes/methods),

Task<string> MyMethodAsync()
{
    Contacts cons = new Contacts();
    TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();

    cons.SearchCompleted += (sender,args) =>
    {
        tcs.TrySetResult(args.Results);
    };
    cons.SearchAsync(String.Empty, FilterKind.None, "My Contact");

    return tcs.Task;
}
like image 145
L.B Avatar answered Jan 22 '23 10:01

L.B