Programming Languages Hacks

Importanti regole per linguaggi di programmazione rilevanti come Java, C, C++, C#…

  • Subscribe

  • Lettori

    I miei lettori abituali

  • Twitter

Convert Event-Based Asynchronous to Task

Posted by Ricibald on November 29th, 2015

Convert Event-Based Asynchronous to Task

The following example shows how to expose an arbitrary sequence of Event-Based Asynchronous Pattern (EAP) operations as one task by using a TaskCompletionSource<TResult>.

The example also shows how to use a CancellationToken to invoke the built-in cancellation methods.

When to use it:

  • you have a mix of Events and Tasks and you need to uniform them
  • you have an Event that needs to be treated using your already-done Task background processing
  • you have a complex flow of Events and you need to simplify the code readability

This extension method do the work:

public static class TaskExt
{
    public static async Task<TEventArgs> FromEvent<TEventArgs>(
        Action<EventHandler<TEventArgs>> registerEvent,
        Action action,
        Action<EventHandler<TEventArgs>> unregisterEvent,
        CancellationToken token)
    {
        var tcs = new TaskCompletionSource<TEventArgs>();
        EventHandler<TEventArgs> handler = (sender, args) => tcs.TrySetResult(args);
        registerEvent(handler);

        try 
        {
            using (token.Register(() => tcs.SetCanceled()))
            {
                action();
                return await tcs.Task;
            }
        }
        finally
        {
            unregisterEvent(handler);
        }   
    }
}

So if you have this class with this event:

public class LdapProvider {
    public event EventHandler<string> Authenticated;
    public void Authenticate(string username, string password) {
        // show the browser...
    }
    //... called back from the browser
    public void AuthenticateCallback(string token) {
        if(Autenticated != null)
            Authenticated(token);
    }
}

You can call this class using events in this way:

// save Token from ldapProvider when the event Autenticate fires
var ldapProvider = new LdapProvider();
ldapProvider.Authenticate("username", "password");
ldapProvider.Authenticated += (sender, token) => this.Token = ldapProvider.Token;
// wait for event be fired...

It can be converted in this way:

public static class LdapProviderExtensions {
    public static async Task<string> AuthenticateAsync(this LdapProvider ldapProvider, string username, string password) { 
        await TaskEx.FromEvent<string> (
            handler => ldapProvider.Authenticated += new EventHandler<string> (handler), 
            () => ldapProvider.Authenticate("username", "password"),
            handler => ldapProvider.Authenticated -= new EventHandler<string> (handler),
            CancellationToken.None);
    }
}


And used in this simple way:

// save Token from ldapProvider when the event Autenticate fires
var ldapProvider = new LdapProvider();
var token = await ldapProvider.AuthenticateAsync("username", "password");
// you have the token without saving it in a property...

Leave a Reply

You must be logged in to post a comment.