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.