The Perl and Raku Conference 2025: Greenville, South Carolina - June 27-29 Learn more

NAME

Venus::Future - Future Class

ABSTRACT

Future Class for Perl 5

SYNOPSIS

package main;
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;
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.