You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Perl Mojolicious中如何从Promise正确渲染响应?

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模板?我并未指定它。
  2. 我已经在渲染内容了,Mojo为什么无法等待?

示例应用代码

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并提交表单,多行输入会触发并发请求。


问题解答

1. 为什么查找example/search.html.ep模板?

Mojolicious的控制器方法如果没有显式返回值,也未完成响应渲染,框架会自动根据控制器和方法名查找对应模板。你的search方法调用了->wait阻塞线程,但异步回调的渲染逻辑未被正确识别,方法执行到末尾无返回值,框架就会进入默认模板渲染流程,因此会查找example/search.html.ep。

2. 为什么Mojo无法等待渲染?

->wait会阻塞事件循环,而Mojolicious是基于事件循环的异步框架,阻塞会导致Promise的then/catch回调无法被事件循环正常处理。当wait结束后,控制器方法已执行完毕,框架认为响应未生成,所以提示"Nothing has been rendered, expecting delayed response"。

正确修改方式

移除->wait,并在Promise链前调用$self->render_later,告知框架延迟渲染响应,等待异步操作完成:

修改后的search方法代码:

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);

    # 通知框架延迟渲染响应
    $self->render_later;

    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;
        $self->render(json => \@json);
    })
    ->catch(
    sub {
        my $err = shift;
        $self->render(text => $err);
    });
}

修改说明

  • render_later:告诉Mojolicious当前请求的响应不会立即生成,需等待后续异步操作完成后发送。
  • 移除->wait:避免阻塞事件循环,让Promise回调能正常在事件循环中执行。
  • 回调内无需返回值:调用$self->render即可触发响应发送,框架会处理后续流程。

内容的提问来源于stack exchange,提问作者h q

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 08:42:26