Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide support for Futures::AsyncAwait #1670

Open
tomgreen98 opened this issue Aug 19, 2022 · 1 comment
Open

Provide support for Futures::AsyncAwait #1670

tomgreen98 opened this issue Aug 19, 2022 · 1 comment

Comments

@tomgreen98
Copy link

tomgreen98 commented Aug 19, 2022

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.

@tomgreen98
Copy link
Author

tomgreen98 commented Aug 19, 2022

Im pretty sure that my workaround of stashing of the package variables is likely to cause other problems since they are not localized correctly after that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant