In a Mojolicious Controller I'd like to chain 2 sets of Promises. The first (e.g. delDB1
& delDB2
) deletes from a source, and when done, the second set (e.g. addBD1
& addDB2
) adds to the source.
Do you add another then
? And if so, how do I pass the new set of Promises?
It doesn't seem right to nest the call inside.
my @promise;
foreach my $code (\&delDB1, \&delDB2) {
my $prom = Mojo::Promise->new;
Mojo::IOLoop->subprocess(
sub {
return $code->($number);
},
sub {
my ($subprocess, $err, @res) = @_;
return $prom->reject($err) if $err;
$prom->resolve(@res);
},
);
push @promise, $prom;
}
Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
Mojo::Promise->all(@promise)
->then(
sub {
my ($delDB1response, $delDB2response) = map {$_->[0]} @_;
### Nest a call for another set of promises?
$app->render(openapi=>{messages=>"success"});
})->catch(
sub ($err) {
warn "### Something went wrong: $err";
})->wait;
Your question is unclear. I think you want the following:
delDB1
.delDB2
.addDB1
.addDB2
.This can be achieved using the following:
Mojo::Promise
->all( delDB1_async(), delDB2_async() )
->then(sub {
return Mojo::Promise->all( addDB1_async(), addDB2_async() );
})
->then(sub {
# Render successful response here...
})
->catch(sub {
# Render failure response here...
})
->wait;
In the above, _async
functions are expected to return a promise.
If we give the promises names for the sake of discussion, the code becomes
my $promise1 = delDB1_async();
my $promise2 = delDB2_async();
my $promise3 = Mojo::Promise->all( $promise1, $promise2 );
my $promise4 = $promise3->then(sub {
my $promise5 = addDB1_async();
my $promise6 = addDB2_async();
my $promise7 = Mojo::Promise->all( $promise5, $promise6 );
return $promise7;
});
my $promise8 = $promise4->then(sub {
# Render successful response here...
});
my $promise9 = $promise8->catch(sub {
# Render failure response here...
});
$promise9->wait;
The key part is that $promise4
becomes fulfilled when $promise7
becomes fulfilled, and with the same value.
But let's say you wanted
delDB1
.addDB1
.delDB2
.addDB2
.This can be achieved using the following:
Mojo::Promise
->all(
delDB1_async()->then(sub { return addDB1_async() }),
delDB2_async()->then(sub { return addDB2_async() }),
)
->then(sub {
# Render successful response here...
})
->catch(sub {
# Render failure response here...
})
->wait;
Example delDB1_async
:
sub delDB1_async {
return Mojo::Promise->new(sub {
my ( $resolve, $reject ) = @_;
Mojo::IOLoop->subprocess(
sub {
...
},
sub {
my ( $subprocess, $err, @res ) = @_;
$reject->( $err ) if $err;
$resolve->( @res );
}
);
});
}
Passing a sub to ->new
is better because exceptions from that code result in in the promise getting rejected. This is great if the code accidentally throws an exception.
...except Mojo::Promise violates the spec to which it claims to abide. So you're currently screwed if the code accidentally throws an exception.