Writing good object-oriented code under AnyEvent

Executive Summary: you either want inversion of control (threads with Coro that block) or a state machine.

You could use Coro, which can convert the infinite ladder into linear code (by inversion of control), e.g. using Coro::rouse_cb/rouse_wait, or some of the Coro::AnyEvent functions:

   do_sth cb => sub { ...

becomes (if the callback is only called once):

   do_sth cb => Coro::rouse_cb;
   my @res = Coro::rouse_wait;

Your only other option is to use a state machine, e.g. using an object (there are many ways to implement state machines, especially in Perl):

my $state = new MyObject;

do_something callback => sub { $state->first_step_done };

And in first_step done, you register a callback for $self->next_state_done etc.

You can also look into some cpan modules, such as AnyEvent::Blackboard or AnyEvent::Tools - I haven't used them myself, but maybe they can help you.

As for condvars, I am personally not so hot about using them as the only means for an API - I prefer callbacks (as condvars are valid callbacks this allows both), but condvars do allow you to raise exceptions in the consumer (via the croak method).


Well, one thing I can think of is using slightly modified Chain of Responsibility pattern:

my $params = {
  user => $user,
  event => $event,
  session => undef
};

my @chain = ('get_session', 'do_stuff', 'save_session', 'emit');

for my $index (0..$#chain) {
  my $current = $chain[$index];
  my $next    = $chain[$index + 1] || undef;
  $self->{$current}($params, 
    on_error => sub { $self->error($params) },
    on_success => sub { $self->{$next}($params) },
  );
}

It's a bit rough, but I hope it shows the point. )


You may want to encapsulate it in a Future object using the Future module. That adds syntactical sugar to make this cleaner.