Сделайте что-нибудь после рендера в mojolicious

Как я могу заставить свой код что-то делать после того, как HypnoToad отправит страницу? (Примечание: я отвечаю на свой вопрос. Я публикую это, потому что StackOverflow указал мне на предыдущий вопрос, который не решил мою проблему напрямую, хотя и содержал подсказку, которая мне была нужна.)

Пример кода:

use Mojolicious::Lite;
get "/index" => sub {
   my $c = shift;
   $c->render("renderThis");
   # Do something after rendering
};
app->start('daemon', '-l', 'http://*:8080');

__DATA__
@@ renderThis.html.ep
% layout  "template" ;
<h1>Hello World</h1>

@@ layouts/template.html.ep
<!DOCTYPE html>
<html><head></head><body>
%= content
</body></html>

render, кажется, буферизует свой http-вывод и отправляет его после завершения блока кода. Я хочу, чтобы что-то выполнялось после отправки страницы. Буферизацию можно наблюдать, поставив вместо комментария «Сделай что-нибудь» следующее.

   sleep 15;
   say "Wow, that was a long time!";

У меня win7, поэтому решение только для unix не сработает.


person Arnold Cross    schedule 30.11.2019    source источник


Ответы (2)


Вы можете прикрепить код к событию завершения транзакции. Большинство других методов не будут гарантированно ожидать отправки ответа, поскольку это происходит асинхронно.

use Mojolicious::Lite;
get "/index" => sub {
   my $c = shift;
   $c->render("renderThis");
   $c->tx->on(finish => sub {
      sleep 15; # this is a really bad idea, use a timer instead
      say "That was a long time, but at least the page got sent quickly.";
   });
};
person Grinnz    schedule 02.12.2019
comment
Спасибо. Это именно то, что я искал. Мне все еще нравится мое хакерское решение... потому что оно хакерское. Но ваш ответ, безусловно, правильный способ сделать это. Я обнаружил, что с таймером мне нужно было поместить небольшое число во временной интервал, чтобы мой фактический код работал правильно. В итоге я использовал 0,01, а не 0. - person Arnold Cross; 03.12.2019

Решение в предыдущем вопросе не работает для меня. (То есть ответ, на который simone ссылается в своем комментарии. Я не пробовал форк-решение, предоставленное мобом.) Моя подсказка пришла из комментария Сухой27. Вот мое решение:

use Mojolicious::Lite;
use Mojo::IOLoop;
get "/index" => sub {
   my $c = shift;
   $c->render("renderThis");
   Mojo::IOLoop->timer(0 => sub {
      sleep 15;
      say "That was a long time, but at least the page got sent quickly.";
   });
};
app->start('daemon', '-l', 'http://*:8080');

__DATA__
@@ renderThis.html.ep
% layout  "template" ;
<h1>Hello World</h1>

@@ layouts/template.html.ep
<!DOCTYPE html>
<html><head></head><body>
%= content
</body></html>

Таймер берет свой код из потока выполнения вызывающего объекта, поэтому вызывающая функция завершается и очищает свои буферы до выполнения кода таймера (даже если аргумент времени короче, чем время, необходимое вызывающему объекту для завершения).

Примечание. Я получил это объяснение из экспериментов, а не из проверки кода, поэтому я не знаю, очищает ли вызывающая программа ВСЕ свои буферы до того, как таймер запустит свой код. Все, что я знаю, это то, что HTTP-ответ рендеринга уходит, а STDOUT сбрасывается на консоль до запуска кода таймера. Я обобщил эти наблюдения, чтобы сделать приведенное выше утверждение.

person Arnold Cross    schedule 30.11.2019