First time poster, be gentle.
I wanted something similar to Mojo::Promise->all, except with an option to discard certain rejected (or, I guess, resolved) promises dependent on rejection value, as determined by caller.
Only using this internally, so nevermind my polluting the Mojo::Promise:: namespace.
package Mojo::Promise::Filter;
use Moose;
extends 'Mojo::Promise';
has 'promises' => (
isa => 'ArrayRef[Mojo::Promise]',
is => 'ro',
required => 1,
);
has 'resolve_filter' => ( # truthy return means *DO* filter out
isa => 'CodeRef',
is => 'ro',
required => 1,
lazy => 1,
default => sub { return sub {}; },
);
has 'reject_filter' => ( # truthy return means *DO* filter out
isa => 'CodeRef',
is => 'ro',
required => 1,
lazy => 1,
default => sub { return sub {}; },
);
has '_promises_state_tracker' => (
# numbered hashref whose primary keys correspond to passed promise-array's indices
# secondary keys are flags for whether each original promise filtered
# and whether promise resolved/rejected,
# plus the original promise itself, e.g. :
# { 0 => { filtered => (undef|1), ror => (undef|1), original_promise => (Mojo::Promise) } }
isa => 'HashRef[HashRef]',
is => 'ro',
lazy => 1,
builder => '_build_promises_state_tracker',
);
sub _build_promises_state_tracker {
my $self = shift;
my $i = 0;
return { map { $i++ => { original_promise => $_ } } @{$self->promises} };
}
sub _resolve_or_reject_self_if_last_child {
my $self = shift;
my @ror = grep { $_->{ror} } values %{$self->_promises_state_tracker};
# gather the thus-far resolved-or-rejected promises
return unless scalar keys %{$self->_promises_state_tracker} == scalar @ror;
# return unless we have as many ror promises as were passed in
my @unfiltered = map { $_->{original_promise} } grep { !$_->{filtered} } values %{$self->_promises_state_tracker};
return Mojo::Promise->all(@unfiltered)->then(sub {
$self->resolve(@_)
})->catch(sub {
$self->reject(@_)
});
}
sub filter {
my $self = shift;
for my $i (0 .. $#{$self->promises}) {
$self->promises->[$i]->then(sub {
$self->_promises_state_tracker->{$i}->{filtered} = 1
if $self->resolve_filter->(@_);
})->catch(sub {
$self->_promises_state_tracker->{$i}->{filtered} = 1
if $self->reject_filter->(@_);
})->finally(sub {
$self->_promises_state_tracker->{$i}->{ror} = 1;
# ror = Resolved Or Rejected
$self->_resolve_or_reject_self_if_last_child();
});
}
return $self;
}
no Moose;
1;
Use as :
my $re_filter = qr/some special text/;
my $reject_filter = sub {
my $v = shift;
return 1 if ref \$v eq 'SCALAR' && $v =~ $re_filter;
};
my $p = Mojo::Promise::Filter->new(
reject_filter => $reject_filter,
promises => \@promises
);
return $p->filter;