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.