Чтобы избежать длительного ожидания, вы можете использовать неблокирующие операции Mojolicious. Вместо выполнения синхронного запроса к внешней системе используйте неблокирующие методы, которые вместо этого запускают некоторый обратный вызов после завершения. Например. чтобы избежать sleep
, мы бы использовали Mojo::IOLoop->timer(...)
.
Вот вариант вашего кода, в котором используются неблокирующие операции и обещания для правильной последовательности функций:
use Mojolicious::Lite;
use Benchmark qw(:hireswallclock);
# example using non-blocking APIs
sub add1 {
my ($a) = @_;
my $promise = Mojo::Promise->new;
Mojo::IOLoop->timer(1 => sub { $promise->resolve($a + 1) });
return $promise;
}
# example using blocking APIs in a subprocess
sub mult2 {
my ($b) = @_;
my $promise = Mojo::Promise->new;
Mojo::IOLoop->subprocess(
sub { # first callback is executed in subprocess
sleep 1;
return $b * 2;
},
sub { # second callback resolves promise with subprocess result
my ($self, $err, @result) = @_;
return $promise->reject($err) if $err;
$promise->resolve(@result);
},
);
return $promise;
}
sub power {
my ($x, $y) = @_;
my $result = Mojo::Promise->new;
Mojo::IOLoop->timer(1 => sub { $result->resolve($x ** $y) });
return $result;
}
any '/' => sub {
my ( $self ) = @_;
my $n = int(rand(5));
my $t0 = Benchmark->new;
my $x_promise = mult2($n);
my $y_promise = add1($n);
my $z_promise = Mojo::Promise->all($x_promise, $y_promise)
->then(sub {
my ($x, $y) = map { $_->[0] } @_;
return power($x, $y);
});
Mojo::Promise->all($x_promise, $y_promise, $z_promise)
->then(sub {
my ($x, $y, $z) = map { $_->[0] } @_;
my $t1 = Benchmark->new;
my $t = timediff($t1, $t0)->real();
$self->render(text => "n=$n, x=$x, y=$y, z=$z;\nT=$t seconds\n");
})
->wait;
};
app->start;
Это намного сложнее, чем ваш код, но выполняется в течение двух секунд вместо трех секунд и не блокирует другие запросы, которые выполняются в то же время. Таким образом, вы можете запросить этот маршрут тридцать раз за один раз и через две секунды получить тридцать ответов!
Обратите внимание, что Promise->all
возвращает обещание со значениями всех ожидаемых обещаний, но помещает значения каждого обещания в массив ref, поэтому нам нужно распаковать их, чтобы получить фактические значения.
Если вы не можете использовать неблокирующие операции, вы можете запустить блокирующий код в подпроцессе, используя Mojo::IOLoop->subprocess(...)
. Вы по-прежнему можете координировать поток данных через промисы. Например. см. приведенную выше функцию mult2
для примера.
metacpan.org/pod/Mojo::IOLoop::ReadWriteFork