The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Venus::Future - Future Class

ABSTRACT

Future Class for Perl 5

SYNOPSIS

  package main;

  use Venus::Future;

  my $future = Venus::Future->new;

  # bless({...}, 'Venus::Future')

  # $future->promise(sub{
  #   my ($resolve, $reject) = @_;
  #   $resolve->result(1);
  # });

  # bless({...}, 'Venus::Future')

  # $future->fulfill;

  # true

DESCRIPTION

This package provides a framework-agnostic "Future" and implementation of the "Promise/A+" pattern for asynchronous programming. The futures are non-blocking and support "suspend" and "resume" allowing them to be used in any asynchronous operating environment.

INHERITS

This package inherits behaviors from:

Venus::Kind::Utility

INTEGRATES

This package integrates behaviors from:

Venus::Role::Buildable

METHODS

This package provides the following methods:

catch

  catch(coderef $on_reject) (Venus::Future)

The catch method registers a rejection handler and returns the future that invokes the handlers.

Since 3.55

catch example 1
  # given: synopsis

  package main;

  my $catch = $future->catch(sub{
    my ($issue) = @_;

    return $issue;
  });

  # bless(..., "Venus::Future")

  # $catch->then(sub{...});

  # bless(..., "Venus::Future")
catch example 2
  # given: synopsis

  package main;

  my $catch = $future->catch(sub{
    my ($issue) = @_;

    return $issue;
  });

  # bless(..., "Venus::Future")

  $future = $future;

  # bless(..., "Venus::Future")

  # $future->reject('Oops!');

  # bless(..., "Venus::Future")

finally

  finally(coderef $on_finally) (Venus::Future)

The finally method registers a finally handler and returns the future that invokes the handlers.

Since 3.55

finally example 1
  # given: synopsis

  package main;

  my $finally = $future->finally(sub{
    my ($data) = @_;

    return $data;
  });

  # bless(..., "Venus::Future")

  # $finally->then(sub{...});

  # bless(..., "Venus::Future")
finally example 2
  # given: synopsis

  package main;

  $future->then(sub{
    $_
  });

  my $finally = $future->finally(sub{
    my ($data) = @_;

    $future->{stash} = $data;

    return $data;
  });

  # bless(..., "Venus::Future")

  $future = $future;

  # bless(..., "Venus::Future")

  # $future->resolve('Hello.');

  # bless(..., "Venus::Future")
finally example 3
  # given: synopsis

  package main;

  $future->then(sub{
    $_
  });

  my $finally = $future->finally(sub{
    my ($data) = @_;

    $future->{stash} = $data;

    return $data;
  });

  # bless(..., "Venus::Future")

  $future = $future;

  # bless(..., "Venus::Future")

  # $future->reject('Oops!');

  # bless(..., "Venus::Future")

fulfill

  fulfill() (Venus::Future)

The fulfill method attempts to fulfill the promise by actuating it, or resuming a previously actuated promise, and returns true if the future has been resolved, i.e. the future is either is_fulfilled or is_rejected, and otherwise returns false.

Since 3.55

fulfill example 1
  # given: synopsis

  package main;

  $future->promise(sub{
    # resolve
    $_[0]->result;
  });

  my $fulfilled = $future->fulfill;

  # true
fulfill example 2
  # given: synopsis

  package main;

  $future->promise(sub{
    # resolve
    $_[0]->result;
  });

  $future->fulfill;

  my $result = $future;

  # bless(..., "Venus::Future")

  # $result->is_fulfilled;

  # true

  # $result->value;

  # undef
fulfill example 3
  # given: synopsis

  package main;

  $future->promise(sub{
    # resolve
    $_[1]->result;
  });

  my $fulfilled = $future->fulfill;

  # true
fulfill example 4
  # given: synopsis

  package main;

  $future->promise(sub{
    # resolve
    $_[1]->result;
  });

  $future->fulfill;

  my $result = $future;

  # bless(..., "Venus::Future")

  # $result->is_rejected;

  # true

  # $result->issue;

  # undef
fulfill example 5
  # given: synopsis

  package main;

  $future->promise(sub{
    # resolve
    $_[0]->result(1);
  })->then(sub{
    return $future->{stash} = $_ * 2; # 2
  })->then(sub{
    return $future->{stash} = $_ * 2; # 4
  })->then(sub{
    return $future->{stash} = $_ * 2; # 8
  });

  $future->fulfill;

  my $result = $future;

  # bless(..., "Venus::Future")

  # $result->is_fulfilled;

  # true

  # $result->value;

  # 1
fulfill example 6
  # given: synopsis

  package main;

  $future->promise(sub{
    # resolve
    $_[0]->result(1);
  });

  $future->then(sub{
    return $future->{stash} = $_ * 2; # 2
  });

  $future->then(sub{
    return $future->{stash} = $_ * 2; # 2
  });

  $future->then(sub{
    return $future->{stash} = $_ * 2; # 2
  });

  $future->fulfill;

  my $result = $future;

  # bless(..., "Venus::Future")

  # $result->is_fulfilled;

  # true

  # $result->value;

  # 1
fulfill example 7
  # given: synopsis

  package main;

  my $pending_future = Venus::Future->new;

  $future->promise(sub{
    # resolve
    $_[0]->result(1);
  })->then(sub{
    return $_
  })->then(sub{
    return $pending_future;
  })->then(sub{
    return $_
  });

  $future->fulfill;

  my @results = ($future, $pending_future);

  # my $result = $future;

  # bless(..., "Venus::Future")

  # $result->is_fulfilled;

  # false

  # $result->is_pending;

  # true

  # $result->value;

  # undef

  # $pending_future->resolve(0);

  # bless(..., "Venus::Future")

  # $pending_future->is_fulfilled;

  # true

  # $result->fulfill;

  # true

  # $result->is_fulfilled;

  # true
fulfill example 8
  # given: synopsis

  package Thenable;

  use Venus::Class;

  sub then {
    my ($self, $resolve, $reject) = @_;

    $resolve->(100);
  }

  package main;

  my $thenable_object = Thenable->new;

  $future->promise(sub{
    # resolve
    $_[0]->result(1);
  })->then(sub{
    return $_
  })->then(sub{
    return $thenable_object;
  })->then(sub{
    return $future->{stash} = $_
  });

  $future->fulfill;

  my @results = ($future, $thenable_object);

  # my $result = $future;

  # bless(..., "Venus::Future")

  # $result->is_fulfilled;

  # true

  # $result->value;

  # 1

is

  is(string $name) (boolean)

The is method take a name and dispatches to the corresponding is_$name method and returns the result.

Since 3.55

is example 1
  # given: synopsis

  package main;

  $future->resolve;

  my $is_fulfilled = $future->is('fulfilled');

  # true
is example 2
  # given: synopsis

  package main;

  my $is_pending = $future->is('pending');

  # true
is example 3
  # given: synopsis

  package main;

  $future->reject;

  my $is_rejected = $future->is('rejected');

  # true

is_fulfilled

  is_fulfilled() (boolean)

The is_fulfilled method returns true if the future has been fulfilled, otherwise returns false.

Since 3.55

is_fulfilled example 1
  # given: synopsis

  package main;

  my $is_fulfilled = $future->is_fulfilled;

  # false
is_fulfilled example 2
  # given: synopsis

  package main;

  $future->resolve;

  my $is_fulfilled = $future->is_fulfilled;

  # true
is_fulfilled example 3
  # given: synopsis

  package main;

  $future->reject;

  my $is_fulfilled = $future->is_fulfilled;

  # false

is_pending

  is_pending() (boolean)

The is_pending method returns true if the future has remained pending, otherwise returns false.

Since 3.55

is_pending example 1
  # given: synopsis

  package main;

  my $is_pending = $future->is_pending;

  # true
is_pending example 2
  # given: synopsis

  package main;

  $future->resolve;

  my $is_pending = $future->is_pending;

  # false
is_pending example 3
  # given: synopsis

  package main;

  $future->reject;

  my $is_pending = $future->is_pending;

  # false

is_promised

  is_promised() (boolean)

The is_promised method returns true if the future a registered promise, otherwise returns false.

Since 3.55

is_promised example 1
  # given: synopsis

  package main;

  my $is_promised = $future->is_promised;

  # false
is_promised example 2
  # given: synopsis

  package main;

  $future->promise;

  my $is_promised = $future->is_promised;

  # false
is_promised example 3
  # given: synopsis

  package main;

  $future->promise(sub{$_[0]->result});

  my $is_promised = $future->is_promised;

  # true

is_rejected

  is_rejected() (boolean)

The is_rejected method returns true if the future has been rejected, otherwise returns false.

Since 3.55

is_rejected example 1
  # given: synopsis

  package main;

  my $is_rejected = $future->is_rejected;

  # false
is_rejected example 2
  # given: synopsis

  package main;

  $future->resolve;

  my $is_rejected = $future->is_rejected;

  # false
is_rejected example 3
  # given: synopsis

  package main;

  $future->reject;

  my $is_rejected = $future->is_rejected;

  # true

issue

  issue() (any)

The issue method returns the result of the "reject" operation once the future has been rejected.

Since 3.55

issue example 1
  # given: synopsis

  package main;

  my $issue = $future->issue;

  # undef

  # $future->is_pending

  # true
issue example 2
  # given: synopsis

  package main;

  $future->reject(0);

  my $issue = $future->issue;

  # 0

  # $future->is_rejected

  # true
issue example 3
  # given: synopsis

  package main;

  $future->reject({fail => 1});

  my $issue = $future->issue;

  # {fail => 1}

  # $future->is_rejected

  # true

new

  new(any @args) (Venus::Future)

The new method instantiates this package and returns a new instance.

Since 3.55

new example 1
  package main;

  my $future = Venus::Future->new;

  # bless(..., "Venus::Future")
new example 2
  package main;

  my $future = Venus::Future->new(sub{
    my ($resolve, $reject) = @_;
    $resolve->result('okay');
  });

  # bless(..., "Venus::Future")

  # $future->is('fulfilled');

  # true

  # $future->value;

  # 'okay'
new example 3
  package main;

  my $future = Venus::Future->new(promise => sub{
    my ($resolve, $reject) = @_;
    $reject->result('boom');
  });

  # bless(..., "Venus::Future")

  # $future->is('rejected');

  # true

  # $future->issue;

  # 'boom'

promise

  promise(coderef $code) (Venus::Future)

The promise method registers a callback executed by the "fulfill" method, which is provided two arguments; the first argument being a Venus::Try instance representing a resolve operaiton; the second argument being a Venus::Try instance representing a reject operaiton; and returns the invocant.

Since 3.55

promise example 1
  # given: synopsis

  package main;

  $future = $future->promise(sub{
    my ($resolve, $reject) = @_;

    $resolve->result('pass');
  });

  # bless(..., "Venus::Future")

  # $future->fulfill;

  # true
promise example 2
  # given: synopsis

  package main;

  $future = $future->promise(sub{
    my ($resolve, $reject) = @_;

    $reject->result('fail');
  });

  # bless(..., "Venus::Future")

  # $future->fulfill;

  # true

reject

  reject(any $issue) (Venus::Future)

The reject method cascades a rejection operation causes the future to be rejected, and returns the invocant.

Since 3.55

reject example 1
  # given: synopsis

  package main;

  my $rejected = $future->reject;

  # bless(..., "Venus::Future")

  # $rejected->status

  # "rejected"

  # $rejected->issue

  # undef
reject example 2
  # given: synopsis

  package main;

  my $rejected = $future->reject('Oops!');

  # bless(..., "Venus::Future")

  # $rejected->status

  # "rejected"

  # $rejected->issue

  # "Oops!"

resolve

  resolve(any $value) (Venus::Future)

The resolve method cascades a rejection operation causes the future to be rejected, and returns the invocant.

Since 3.55

resolve example 1
  # given: synopsis

  package main;

  my $fulfilled = $future->resolve;

  # bless(..., "Venus::Future")

  # $fulfilled->status

  # "fulfilled"

  # $fulfilled->value

  # undef
resolve example 2
  # given: synopsis

  package main;

  my $fulfilled = $future->resolve('Great!');

  # bless(..., "Venus::Future")

  # $fulfilled->status

  # "fulfilled"

  # $fulfilled->value

  # "Great!"

status

  status() (any)

The status method returns the status of the future. Valid statuses are fulfilled, pending, and rejected.

Since 3.55

status example 1
  # given: synopsis

  package main;

  my $status = $future->status;

  # "pending"
status example 2
  # given: synopsis

  package main;

  $future->resolve(0);

  my $status = $future->status;

  # "fulfilled"
status example 3
  # given: synopsis

  package main;

  $future->reject(0);

  my $status = $future->status;

  # "rejected"

then

  then(coderef $fulfill, coderef $reject) (Venus::Future)

The then method registers fulfillment and rejection handlers and returns the future that invokes the handlers.

Since 3.55

then example 1
  # given: synopsis

  package main;

  my $new_future = $future->then(sub{
    # fulfillment handler
    $_
  });

  # "Venus::Future"

  # $new_future->is_pending;

  # true
then example 2
  # given: synopsis

  package main;

  my $new_future = $future->then(sub{
    # fulfillment handler
    $_
  },
  sub{
    # rejection handler
    $_
  });

  # "Venus::Future"

  # $new_future->is_pending;

  # true
then example 3
  # given: synopsis

  package main;

  my $new_future = $future->then(undef, sub{
    # rejection handler
    $_
  });

  # "Venus::Future"

  # $new_future->is_pending;

  # true
then example 4
  # given: synopsis

  package main;

  my $new_future = $future->then(sub{
    # fulfillment handler
    $_
  });

  # "Venus::Future"

  # $new_future->is_pending;

  # true

  $future = $future;

  # "Venus::Future"

  # $new_future->is_pending;

  # true
then example 5
  # given: synopsis

  package main;

  my $new_future = $future->then(sub{
    # fulfillment handler
    $_
  },
  sub{
    # rejection handler
    $_
  });

  # "Venus::Future"

  # $new_future->is_pending;

  # true

  $future = $future;

  # "Venus::Future"

  # $new_future->is_pending;

  # true
then example 6
  # given: synopsis

  package main;

  my $new_future = $future->then(undef, sub{
    # rejection handler
    $_
  });

  # "Venus::Future"

  # $new_future->is_pending;

  # true

  $future = $future;

  # "Venus::Future"

  # $new_future->is_pending;

  # true

value

  value() (any)

The value method returns the result of the "resolve" operation once the future has been fulfilled.

Since 3.55

value example 1
  # given: synopsis

  package main;

  my $value = $future->value;

  # undef

  # $future->is_pending

  # true
value example 2
  # given: synopsis

  package main;

  $future->resolve(1);

  my $value = $future->value;

  # 1

  # $future->is_fulfilled

  # true
value example 3
  # given: synopsis

  package main;

  $future->resolve({pass => 1});

  my $value = $future->value;

  # {pass => 1}

  # $future->is_fulfilled

  # true

wait

  wait(number $timeout) (Venus::Future)

The wait method blocks the execution of the current process until a value is received. If a timeout is provided, execution will be blocked until a value is received or the wait time expires. If a timeout of 0 is provided, execution will not be blocked. If no timeout is provided at all, execution will block indefinitely.

Since 3.55

wait example 1
  # given: synopsis

  package main;

  $future->promise(sub{
    # resolve
    $_[0]->result;
  });

  $future = $future->wait(0);

  # bless(..., "Venus::Future")

  # $future->is_fulfilled;

  # true
wait example 2
  # given: synopsis

  package main;

  $future->promise(sub{
    # never fulfilled
  });

  $future->wait(1);

  # Exception! (isa Venus::Future::Error) (see error_on_timeout)

ERRORS

This package may raise the following errors:

error: error_on_timeout

This package may raise an error_on_timeout exception.

example 1

  # given: synopsis;

  my $input = {
    throw => 'error_on_timeout',
    timeout => 10,
  };

  my $error = $future->try('error', $input)->error->result;

  # my $name = $error->name;

  # "on_timeout"

  # my $message = $error->render;

  # "Future timed-out after 10 seconds"

  # my $timeout = $error->stash('timeout');

  # 10

AUTHORS

Awncorp, awncorp@cpan.org

LICENSE

Copyright (C) 2022, Awncorp, awncorp@cpan.org.

This program is free software, you can redistribute it and/or modify it under the terms of the Apache license version 2.0.