Perl Mojolicious,从Promise中呈现响应的正确方法是什么?

cxfofazt  于 2023-04-21  发布在  Perl
关注(0)|答案(1)|浏览(113)

在Mojolicious中,呈现Promise响应的正确方法是什么?在下面的代码中,我得到:

[2023-04-12] [trace] Template "example/search.html.ep" not found
[2023-04-12] [trace] Nothing has been rendered, expecting delayed response

1.它为什么要找example/search.html.ep?我又没问。
1.我正在渲染内容... Mojo等不及了?
我的示例应用程序有3个文件:

1. script/my_app

#!/usr/bin/env perl

use strict;
use warnings;

use Mojo::File qw(curfile);
use lib curfile->dirname->sibling('lib')->to_string;
use Mojolicious::Commands;

Mojolicious::Commands->start_app('MyApp');

2. lib/MyApp.pm

package MyApp;
use Mojo::Base 'Mojolicious', -signatures;

sub startup ($self) {
    $self->secrets('s3cret');
    my $r = $self->routes;
    $r->get('/')->to('Example#index');
    $r->post('/')->to('Example#search');
}

1;

3. lib/MyApp/Controller/Example.pm

package MyApp::Controller::Example;
use Mojo::Base 'Mojolicious::Controller', -signatures;

sub index ($self)
{
    return $self->render(inline => '<html><body><form method="post"><textarea name="numbers" maxlength="11">123</textarea><button type="submit">Go</button></form></body></html>');
}

sub search ($self)
{
    my $v = $self->validation;
    $v->required("numbers");
    return $self->render(text=>"Validation Error") if $v->has_error;
    my $numbers = $v->param("numbers");
    my @numbers = split(/\r?\n/, $numbers);

    Mojo::Promise
    ->map(
    {concurrency => 2},
    sub {
        $self->ua->get_p("https://httpbin.org/delay/1?q=$_" => {'api-key'=>'shhh'});
    }, @numbers)
    ->then(
    sub{
        my @results = @_;
        my @json = map { $_->[0]->res->json } @results;
        return $self->render(json => \@json);
    })
    ->catch(
    sub {
        my $err = shift;
        return $self->render(text => $err);
    })
    ->wait;
    #return $self->render(text => "This shall result is 'Unhandled rejected promise: A response has already been rendered'");
}

1;

要运行该应用程序,我需要:

morbo script/my_app

然后导航到http://localhost:3000并发布表单。多行将导致并发调用。

js81xvg6

js81xvg61#

在调用Promise之前添加一个render_later

$self->render_later();

Mojo::Promise
  ->map(
    {concurrency => 2},
...

如果没有它,当函数search退出时,Mojolicious将默认渲染关联的模板(example/search.html.ep),并失败,因为此模板不存在(请参阅Mojolicious::Guide::Rendering中的自动渲染)。render_later禁用此自动渲染,基本上告诉Mojolicious“保持连接打开,我稍后会渲染”。
你的困惑可能来自于wait的使用,它在这个上下文中并没有真正的“等待”。如果你在整个promise的前后添加一个print:

say "Before";
Mojo::Promise->map(...);
say "After";

然后日志将是:

[2023-04-13 09:23:08.78601] [1201283] [trace] [rR7TxcCsrygT] POST "/"
[2023-04-13 09:23:08.78692] [1201283] [trace] [rR7TxcCsrygT] Routing to controller "MyApp::Controller::Example" and action "search"
Before
After
[2023-04-13 09:23:08.78953] [1201283] [trace] [rR7TxcCsrygT] Template "example/search.html.ep" not found
...

这确实表明wait并没有真正等待(如果你不相信,你甚至可以在thencatch中添加打印:它们将在Before之后打印)。
看看wait的文档,它说:
启动“ioloop”,并在promise被实现或拒绝后再次停止它,当“ioloop”已经在运行时不做任何事情。
在这里,由于您正在运行服务器,IOLoop始终运行,因此wait什么也不做。这实际上是可取的:如果wait实际上要等待,那么使用Promise就没有意义了。
另一方面,当您单独使用Mojo::Promise时(即不在Mojolicious服务器中),您可能需要手动使用wait运行IOLoop,以便在promise完成之前不退出脚本。

相关问题