Description
Just want to say, I really like Dancer2. But I hit a limitation with one area of its support for recent improvements to async style code calling.
I have been trying to use Future::AsyncAwait
with the delayed
DSL something like:
package MyService;
# NOTE: Running this under Twiggy to get the event loop to work.
use Dancer2;
use AnyEvent; # for the event loop
use AnyEvent::HTTP; # for async http calls
use Promise::ES6; # modern promise responses
use Future::AsyncAwait; # for even more modern handling of async.
post '/' => sub {
my $url = "...";
delayed async {
# just a deferred promise implementation. pick anyone you prefer.
my ( $resolve, $reject );
my $promise = Promise::ES6->new(
sub ( $res, $rej ) { ( $resolve, $reject ) = ( $res, $rej ) }
);
# start an async call
AnyEvent::HTTP::http_get( $url, %opts, sub {
my ($body, $header) = @_;
$header->{Status} =~ /^[23]\d\d/ ? $resolve->($body) : $reject->($body);
});
my $response = await $promise;
# Problems starts here.
delayed {
content($response);
done();
};
}
}
1;
The problem is that i get an error after the await
that the inner delayed
DSL can not be used where they are located. Looks like Dancer2 does a check to see if a package variable that was localized is defined, which gets lost during the await. If i manually copy that variable and the others before the await
call, I can make it work if I restore them after the await
:
my $stash = stash_package_variables();
my $response = await $promise;
restore_package_variables($stash);
The problem seems to revolve around the design of the delayed
underlying objects Dancer2::Core::Response::Delayed
in that this object captures the needed package variables in a block locally and then reuses the object in the subsequent delayed
calls. But the design for delayed assumes that you are wrapping all your async
callbacks in delayed DSL. That not possible in async/await style since the underlying callbacks are not usually exposed.
I guess technically in my example I could wrap the callback for the http_get call, but my actual implementation is with a HTTP library that already returns the promise and you should be able to use this with any library that returns promises without having to change the library to be Dancer2
aware.
Another minor issue is that the top level await can not be wrapped in async like I have it in the current example. You have to instead manually unwrap the async call like:
delayed {
Future->unwrap(async sub {
})->();
};
It would be better to natively support async/await style directly in the DSL and adjust the delayed design to be smarter about how to handle the transitions across the await
boundaries.