Asynchronous Functions

Asynchronous Commands are a useful way to organize asynchronous activities, but they don’t have any way to pass values or control back to a caller. This post contains a simple Asynchronous Function library that lets you do that. In C# you call an Asynchronous Function like:

 void CallingMethod(...) {
    ... do some things ...
    IAsyncFunction<String> httpGet=new HttpGet(... parameters...);
    anAsynchronousFunction.Execute(CallbackMethod);
}

void CallbackMethod(CallbackReturnValue<String> crv) {
    if (crv.Error!=null) { ... handle Error,  which is an Exception ...}
    String returnValue=crv.Value;
    ... do something with the return value ...
}

We’re using generics so that return values can be passed back in a type safe manner. The type of the return value of the asynchronous function is specified in the type parameter of IAsyncFunction and CallbackReturnValue.

Asynchronous functions catch exceptions and pass them back in  in the CallbackReturnValue.  This makes it possible to propagate exceptions back to the caller,  as in synchronous functions.  The code to do this must has to be manually replicated in each asynchronous function,  however,  the code can be put into a wrapper delegate.

You could do the same thing in Java, but the CallbackMethod would need to be a class that implements an interface rather than a delegate.

In C# it takes one class, one interface and one delegate to make this work:

     public delegate void CallbackFunction<ReturnType>(CallbackReturnValue<ReturnType> value);

    public class CallbackReturnValue<ReturnType> {
        public ReturnType Value {get; private set; }
        public Exception Error {get; private set; }

        protected CallbackReturnValue() { }

        public static CallbackReturnValue<ReturnType> CreateOk(ReturnType value) {
            CallbackReturnValue<ReturnType> crv=new CallbackReturnValue<ReturnType>();
            crv.Value=value;
            return crv;
        }

        public static CallbackReturnValue<ReturnType> CreateError(Exception error) {
            CallbackReturnValue<ReturnType> crv = new CallbackReturnValue<ReturnType>();
            crv.Error = error;
            return crv;
        }
    }

    public interface IAsyncFunction<ReturnType>
    {
        void Execute(CallbackFunction<ReturnType> callback);
    }

Here’s a simple example of an asynchronous function implementation:

    public class HttpGet : IAsyncFunction<String>
    {
        private Uri Path;
        private CallbackFunction<String> OuterCallback;
        private HttpWebRequest Request;

        public HttpGet(Uri path)
        {
            Path = path;
        }

        public void Execute(CallbackFunction<String> outerCallback)
        {
            OuterCallback = outerCallback;
            try
            {
                HttpWebRequest Request = (HttpWebRequest)WebRequest.Create(Path);
                Request.Method = "GET";
                Request.BeginGetRequestStream(InnerCallback,null);
            }
            catch (Exception ex)
            {
                OuterCallback(CallbackReturnValue<String>.CreateError(ex));
            }
        }

        public void InnerCallback(IAsyncResult result)
        {
            try
            {
                HttpWebResponse response = (HttpWebResponse) Request.EndGetResponse(result);
                TextReader reader = new StreamReader(response.GetResponseStream());
                OuterCallback(CallbackReturnValue<String>.CreateOk(reader.ReadToEnd()));
            } catch(Exception ex) {
                OuterCallback(CallbackReturnValue<String>.CreateError(ex));
            }
        }
    }

Asynchronous Functions provide a simple and typesafe way to build asynchronous functions by composing them out of simpler asynchronous functions, and it provides a reasonable way to emulate the usual bubbling of exceptions that allows callers to catch exceptions thrown inside a callee. (Without this kind of intervention, exceptions in the callback functions propagate in the reverse of the usual direction!) Over the next few posts, we’ll talk about how asynchronous execution really works, which is essential for getting good results with Asynchronous Functions.