Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl6 Making an observable timer

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?

like image 702
Mikkel Avatar asked Jun 28 '18 09:06

Mikkel


2 Answers

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 emits 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.

like image 92
timotimo Avatar answered Sep 21 '22 23:09

timotimo


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 Promises whatsoever, so if your goal is to know more about Promises, 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.

like image 30
Elizabeth Mattijsen Avatar answered Sep 23 '22 23:09

Elizabeth Mattijsen