I am trying to write an observer pattern using a timer class which invoke its subscribers at a given interval. All while trying to understand the concept of promises in Perl6.
class Timer does Observable
{
has Promise $!p;
has Int $!interval;
has Bool $!active = False;
submethod BUILD(Int :$!interval) {}
method start {
$!p = Promise.start( {
$!active = True;
loop {
sleep($!interval);
@!action_listeners>>.actionPerformed(ActionEvent.new);
if !$!active {$!p.keep}
}
});
}
method stop {
$!active = False;
}
}
The Observer role simply just have an array with subscribers. Granted I should have made a method for updating all subscribers within the role.
role Observable {
has ActionListener @!action_listeners;
method addActionListener(ActionListener $al){
@!action_listeners.push($al);
}
method removeActionListener{
@!action_listeners.pop;
}
}
The ActionListener role simply has a actionPerformed method. The class ActionEvent might not be nessesary, it is simply an empty class at the moment.
role ActionListener
{
method actionPerformed(ActionEvent $e) { ... }
}
Running from a script:
my Subscriber $s = Subscriber.new;
my Timer $t = Timer.new(interval => 1);
$t.start;
$t.addActionListener($s);
$t.addActionListener(Subscriber.new);
.... #doing stuff that lasts for a while
$t.stop;
The subscriber class implements the ActionListener role (has a method called actionPerformed).
Though this work fine: The actionPerformed method of the subscribers get invoked until I call the stop method of the timer. While having no proper way to remove a given subscriber. Also I was wondering if there is a better way to keep/break a promise, from outside given the code loops infinitively.
I am basically wondering if I might be missing out on built in features in Perl6? Am I reinventing the wheel?
To answer the question of re-inventing the wheel even further: You should have a look at Supply. Supply
is the Perl 6 term for Observable
, and Tap
is the Perl 6 term for Subscription
.
In order to get an observable that invokes the subscribers in a regular interval, you can use the interval class method of the Supply type. You would call .tap
on the result of the .interval
call to register a code block to be run whenever the Supply
emit
s a value and the result of the .tap
call is the Tap
object that you can close the subscription with.
These talk slides by Jonathan Worthington might be interesting if you want to know more about Supplies.
To answer your question "Am I reinventing the wheel?". Possibly.
You can actually ask the current ThreadPoolScheduler
(aka $*SCHEDULER
) to cue a piece of code to be run at an interval:
my $canceller = $*SCHEDULER.cue( { say $++ }, every => 0.2 );
sleep 2;
# 0 1 2 3 4 5 6 7 8 9
$canceller.cancel;
say "stopped"; # no numbers should be seen afterward
# stopped
sleep 1; # keep from exiting immediately
This does not involve any Promise
s whatsoever, so if your goal is to know more about Promise
s, then this is not the way to go. If you want to create an observer pattern, this might be the cleanest way of creating it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With