Evo

 view release on metacpan or  search on metacpan

lib/Evo/Promise/Role.pm  view on Meta::CPAN

package Evo::Promise::Role;
use Evo -Class;
use Evo '-Promise::Sync; -Lib try; -Promise::Const *; -Promise::Deferred';
use Evo 'Carp croak; Scalar::Util blessed';

requires 'postpone';

# https://promisesaplus.com/

has $_, optional for qw(d_v d_locked d_fh d_rh d_settled);
has 'd_children' => ro, sub { [] };
has 'state' => PENDING;

#sub assert { shift or croak join '; ', caller() }

#sub value($self) {
#  croak "$self isn't fulfilled" unless $self->state eq FULFILLED;
#  $self->d_v;
#}
#
#sub reason($self) {
#  croak "$self isn't rejected" unless $self->state eq REJECTED;
#  $self->d_v;
#}

## CLASS METHODS
sub promise ($me, $fn) {
  my $d = Evo::Promise::Deferred->new(promise => my $p = $me->new());
  try {
    $fn->(sub { $d->resolve(@_) }, sub { $d->reject(@_) });
  }
  sub($e) {
    $d->reject(@_);
  };
  $p;
}

sub deferred($me) { Evo::Promise::Deferred->new(promise => $me->new()); }

sub resolve ($me, $v) {
  my $d = Evo::Promise::Deferred->new(promise => $me->new());
  $d->resolve($v);
  $d->promise;
}

sub reject ($me, $v) {
  my $d = Evo::Promise::Deferred->new(promise => $me->new());
  $d->reject($v);
  $d->promise;
}

sub race ($me, @prms) {
  my $d = Evo::Promise::Deferred->new(promise => $me->new());
  my $onF = sub { $d->resolve(@_) };
  my $onR = sub { $d->reject(@_) };
  foreach my $cur (@prms) {
    if (ref $cur eq 'Evo::Promise::Class') {
      $cur->then($onF, $onR);
    }
    else {
      # wrap with our promise
      my $wd = Evo::Promise::Deferred->new(promise => $me->new());
      $wd->promise->then($onF, $onR);
      $wd->resolve($cur);
    }
  }

  $d->promise;
}

lib/Evo/Promise/Role.pm  view on Meta::CPAN

  my $pending = @prms;

  my @result;
  my $onR = sub { $d->reject($_[0]) };

  for (my $i = 0; $i < @prms; $i++) {
    my $cur_i = $i;
    my $cur_p = $prms[$cur_i];
    my $onF   = sub { $result[$cur_i] = $_[0]; $d->resolve(\@result) if --$pending == 0; };

    if (ref $cur_p eq 'Evo::Promise::Class') {
      $cur_p->then($onF, $onR);
    }
    else {
      # wrap with our promise
      my $wd = Evo::Promise::Deferred->new(promise => $me->new());
      $wd->promise->then($onF, $onR);
      $wd->resolve($cur_p);
    }
  }
  $d->promise;
}

### OBJECT METHODS

sub finally ($self, $fn) {
  my $d   = Evo::Promise::Deferred->new(promise => ref($self)->new);
  my $me  = ref($self);
  my $onF = sub($v) {
    $d->resolve($fn->());    # need pass result because it can be a promise
    $d->promise->then(sub {$v});
  };
  my $onR = sub($r) {
    $d->resolve($fn->());    # see above
    $d->promise->then(sub { $me->reject($r) });
  };
  $self->then($onF, $onR);
}

sub catch ($self, $cfn) {
  $self->then(undef, $cfn);
}

sub spread ($self, $fn) {
  $self->then(sub($ref) { $fn->($ref->@*) });
}


sub then {
  my ($self, $fh, $rh) = @_;
  my $p = ref($self)->new(ref($fh) ? (d_fh => $fh) : (), ref($rh) ? (d_rh => $rh) : ());
  push $self->d_children->@*, $p;
  $self->d_traverse if $self->d_settled;
  $p;
}

### DRIVER INTERNAL METHODS

sub d_lock_in ($self, $parent) {

  #assert(!$self->d_locked);
  #assert(!$self->d_settled);
  unshift $parent->d_children->@*, $self->d_locked(1);
}

sub d_fulfill ($self, $v) {

  #assert(!$self->d_settled);
  $self->d_settled(1)->state(FULFILLED)->d_v($v);
}

sub d_reject ($self, $r) {

  #assert(!$_[0]->d_settled);
  $self->d_settled(1)->state(REJECTED)->d_v($r);
}

# 2.3 The Promise Resolution Procedure
# 2.3.3.2, 2.3.3.4 doesn't make sense in perl (in real world)
# Changed term obj or func to blessed obj and can "then"
sub d_resolve ($self, $x) {

  #assert(!$self->d_settled);

  while (1) {

    # 2.3.4 but means not a blessed object
    return $self->d_fulfill($x) unless blessed($x);


    # 2.3.1
    return $self->d_reject('TypeError') if $x && $self eq $x;

    # 2.3.2 promise
    if (ref $x eq ref $self) {
      $x->d_settled
        ? $x->state eq FULFILLED
          ? $self->d_fulfill($x->d_v)
          : $self->d_reject($x->d_v)
        : $self->d_lock_in($x);
      return;
    }

    if ($x->can('then')) {
      my $sync = Evo::Promise::Sync->new(promise => $self)->try_thenable($x);
      return unless $sync->should_resolve;
      $x = $sync->v;    # and next, but it's already last in loop
      next;
    }

    # 2.3.3.4
    return $self->d_fulfill($x);
  }
}

# reject promise and call traverse with the stack of children
sub d_reject_continue ($self, $reason) {
  $self->d_reject($reason);
  $self->d_traverse;
}

sub d_resolve_continue ($self, $v) {
  $self->d_resolve($v);



( run in 2.340 seconds using v1.01-cache-2.11-cpan-140bd7fdf52 )