ILoggable

A place to keep my thoughts on programming

February 15, 2011 .net

Ok, so there are still delegates you have to define

Yesterday I needed to create a bit of code that should retry an http request on failure and i wanted this to happen in a non-blocking fashion, so a plain loop was out. Using DReAM's Plug, I can easily set up a .WhenDone handler, but I need something to call back recursively. I could have created a new method that calls itself recursively, but inspired by my recent scala reading, i wanted to define the function to execute to exist only in the scope of the current function. The resulting code was this:

public void Restart(string wikiId) {
    Action restarter = null;
    var retry = 0;
    restarter = () => {
        retry++;

        // call the stop endpoint
        _self.At("host", "stop", wikiId)
            .Post(new Result<DreamMessage>()).WhenDone(
                m => {

                    // if the endpoint was unavailable and we've got retries left ...
                    if(m.Status == DreamStatus.ServiceUnavailable && retry <= 3) {

                        // ... sleep for 2 seconds ...
                        Async.Sleep(2.Seconds()).WhenDone(r => {

                            // ... then try again
                            restarter();
                        });
                    }
                },
                e => {
                    // plug's don't throw exceptions, they communicate failure in the resulting message
                }
            );
    };

    // kick the restart attempt off
    restarter();
}

Completely non-blocking, yay. Of course, like most continuation based async code, it has a tendency to walk off the right side of the screen, but that's a different topic.

Discussing this pattern, Steve suggested that instead of explictly calling myself to execute the next retry, the Action should take another action as its argument. By passing continuation in, any kind of continuation could be called, even a no-op that terminates the chain.

Ok, cool, let's just change the signature of restarter to Action<Action>… no wait, it need to be an action that takes and action that takes an action, that…. hrm, you see the problem…

Action<Action<Action<Action<ad infinitum>>>>

I needed a recursive delegate definition and that is simply not possible with Func<> and Action<>. I needed something like:

delegate void Recursor(Recursor continuation)

So, unlike my previous statement, there are still scenarios where you have to write a delegate 🙂

1 to “Ok, so there are still delegates you have to define”

  1. tobi says...

    2.Seconds()
    oh god..^^

Leave a comment