在AnyEvent下编写好的面向对象代码

Dal*_*aen 8 perl asynchronous anyevent

我们正在构建一个具有复杂逻辑的大型应用程序,它由模块组成.我曾经用更简单的方法构建更大规模的方法,例如,

# fig. 1   
package Foo;
sub highlevel {
    my ($self, $user, $event) = @_;
    my $session = $self->get_session($user);
    my $result = $self->do_stuff($session, $event);
    $self->save_session($session);
    return $result;
};
Run Code Online (Sandbox Code Playgroud)

(当然这是简化的).返回结果,抛出异常,每个人都很开心.

现在,我们正在转向AnyEvent.我的模块不是最高级别,所以我做不到

# fig. 2
my $cv = AnyEvent->condvar;
# do stuff
return $cv->recv;
Run Code Online (Sandbox Code Playgroud)

到目前为止我见过的大多数AE模块都是这样的:

# fig. 3
$module->do_stuff( $input, 
    on_success => sub { ... }, 
    on_error => sub { ... }
);
Run Code Online (Sandbox Code Playgroud)

所以我完成了对低级方法的重写并尝试继续使用highlevel()和...

# fig. 4
package Foo;
sub highlevel {
    my ($self, $user, $event, %callbacks) = @_;
    my $done = $callbacks{on_success};
    my $error = $callbacks{on_error};
    $self->get_session( $user,
        on_error => $error,
        on_success => sub {
             my $session = shift;
             $self->do_stuff( $session, $event,
                  on_error => $error,
                  on_success => sub { 
                       my $result = shift;
                       $self->save_session( $session,
                            or_error => $error,
                            on_success => sub { $done->($result); }
                       );
                  }
              );
          }
     );
 };
Run Code Online (Sandbox Code Playgroud)

不完美.我称之为"无限阶梯".

现在接下来我能想到的是一个临时状态机,其中highlevel()被分解为_highlevel_stage1(),_ highlevel_stage2()等.但这也不能满足我(它是不可维护的,并且考虑好了)名字而不是stageXX让我很头疼).

我们已经在研究一款功能齐全的状态机来驱动整个应用程序,但是为每次交互添加一个过渡对我来说看起来有点过于慷慨.

所以问题是:编写实现业务逻辑的模块(图1)在AnyEvent应用程序中运行的最佳实践是什么(图3)?

Rei*_*ica 8

执行摘要:您要么需要反转控制(使用Coro阻塞的线程),要么需要状态机.

您可以使用Coro,它可以将无限梯形图转换为线性代码(通过控制反转),例如使用Coro :: rouse_cb/rouse_wait或一些Coro :: AnyEvent函数:

   do_sth cb => sub { ...
Run Code Online (Sandbox Code Playgroud)

成为(如果回调只调用一次):

   do_sth cb => Coro::rouse_cb;
   my @res = Coro::rouse_wait;
Run Code Online (Sandbox Code Playgroud)

您唯一的另一种选择是使用状态机,例如使用对象(有很多方法可以实现状态机,特别是在Perl中):

my $state = new MyObject;

do_something callback => sub { $state->first_step_done };
Run Code Online (Sandbox Code Playgroud)

在first_step完成后,您为$ self-> next_state_done等注册回调.

您还可以查看一些cpan模块,例如AnyEvent :: Blackboard或AnyEvent :: Tools - 我自己没有使用它们,但也许它们可以帮助您.

至于condvars,我个人并不热衷于使用它们作为API的唯一手段 - 我更喜欢回调(因为condvars是有效的回调,这允许两者),但condvars允许你在消费者中引发异常(通过croak方法).


cst*_*mas 3

您可能希望使用该Future模块将其封装在 Future 对象中。这增加了语法糖以使这个更干净。