http://qs321.pair.com?node_id=1151141

QuillMeantTen has asked for the wisdom of the Perl Monks concerning the following question:

Greetings and a merry christmas everyone,
I come to you tonight because I am encountering quit a hurdle in my understanding.

I recently started playing an online game which requires the player to code programs that interacts with a server api. After getting to know said API I discovered one could use websockets. So on I went, rummaging through my local CPAN distributor for the right module, trying this or that until I found AnyEvent::Websocket::Client.

This is my first time with event driven programming so please bear with my ignorance:
Update: From what I understand, once this is called, an event loop is entered and everything happens in the callback routine

I rewrote my code following this behaviour and it got working.
Now, what if I need to keep an eye on two event loops?
Do I need to spawn a new process and have it pipe data to me?
I am thinking about something along those lines:

from where I stand a possible solution would be:
1 Acting process that spawns two websocket listening children, each child openend with a handle so the father can use the diamond operator.

Now a real mindfuck: what design to choose from:
1) a shared variable referencing my local copy of the overall system state, which gets updated by both processes and periodically checked by their father? 2) a pipe system from the children to their father that feeds him new info?
#this code should block if nothing comes from child 2 and during that +time it #may receive data from child1 that wont be acted upon while(<$child1>){ try something...will block if nothing comes from child 1 my $line = <$child2> while(!defined $line){ $line = <$child2>; do something else } }
Old code
while(1){ print "recving\n"; my $hash = $trader->{venues}->{$venues[0]}->{ticker_tape}->recv; print "recved\n"; my $parsed = parse_json($hash); print "\n" x 5; print "$hash"; print "\n" x 5; }
from what I understand, this code should block on the line begining with
"my hash, such and such"
until it receives the signal from the following snippet:
$self->{venues}->{$i}->{client} = AnyEvent::WebSocket::Client->new +; my $cv = $self->{venues}->{$i}->{client}->connect("wss://api.s +tockfighter.io/ob/api/ws/$self->{account}/venues/$i/tickertape")->cb( +sub{ our $connection = eval { shift->recv }; if($@) { warn $@; return; } $connection->on(each_message => sub { my($connection, $message) = @_; my $decode = parse_json($message->{body}); print "got a message ticker!\n"; $self->{venues}->{$i}->{ticker_tape}->send($message +->{body}); }); $connection->on(finish => sub { # $connection is the same connection object my($connection) = @_; $connection->close; }); });
said signal containing a json string.
Thing is: I only receive ONE string, the message
"got a message ticker"
appears only once which indicates that the callback is only called the first time. then the message gets repeated over and over afterward. I think I am missing some important concept regarding this type of programming and I would be most grateful if someone could point it out.

Replies are listed 'Best First'.
Re: AnyEvent Question
by Anonymous Monk on Dec 24, 2015 at 23:05 UTC

    I come to you tonight because I am encountering quit a hurdle in my understanding.

    I would start with this example Re^3: real-time output from Mojolicious WebSockets?

    Use it to understand what is going on

    Notice how there are no infinite while loops, you register your callbacks/handlers, start a connection, and then it just runs on its own, you never call recv, a message gets sent, a message gets received, its just runs and runs and runs ....

Re: AnyEvent Question
by gnosti (Chaplain) on Dec 25, 2015 at 20:12 UTC
    To add to the above, I can suggest you might do well to experiment with some event programming on your local machine, without websockets at all, to clarify the main concepts of this style of programming. See 'man anyevent' for examples. You can set up a watcher for each type of event you want to act on.
Re: AnyEvent Question
by QuillMeantTen (Friar) on Dec 26, 2015 at 08:33 UTC

    Greetings,
    Thanks for the tips, I have started reading and I think I am starting to get it... I would like to summarize my present understanding so that you can correct me:

    When you make the call, give the callbacks and such you are stating that "in case of event on this channel, this is what I want to be done".
    This decision is completely independent from any condvar send or recv, since such action does not have to involve any of this.
    If you want your program to wait for events on a specific channel (or combinations thereof) then you start putting condvars, with a producer inside the callback and a receiver wherever you want the program to block until the event happens.

    The trick being, your program state is now (or at least in that paradigm) dictated by whatever data comes down the pipes and how you want to deal with it. One could classify such data as either blocking or non blocking. Non blocking data being able to change the program state in small ways, blocking data requiring the program to wait for it and then react.

    Now onto the "main loop", such program could do completely without one (and even without a condvar if the window in which the program looks for the event is very small?) if it only waits for one event.

    Classic while or for loops are useless for this since those loops depend on the internal state of the program, not the state of an external system which data comes down the pipes.
    Hence when writing the main program one has to state the different events one is watching and how they will change the program state.

    such a loop, when using anyevent is defined like this:

    use Anyevent; ... ... some more code watching for events ... ... Anyevent::condvar->recv
    One has to understand though that this loop wont be interrupted whatever the internal state of the program might become, if one wanted the program to be able to terminate itself one would have to use a condvar that is produced somewhere inside the loop:
    use Anyevent; ... some code ... event loop with a shoot_myself_in_the_mouth condvar ... shoot_myself_in_the_mouth->recv;


    Now I think I can discern more clearly the design challenges I am faced with:
    Waiting for an internally specified time, checking an internal variable for changes that could have happened during that time.

    Acting upon it.

    Blocking on another event channel that would indicate the effects of my actions

    going back to square one until externally terminated:
    connect_to_event_source_one; connect_to_event_source_two; use Anyevent; sleep($sometime); if($internal_variable == $something_else){ do_some_action; event_source_two recv; if($internal_variable2 == $new_expected_external_state) { yet_another_action; } } Anyevent::condvar->recv;
    Update: some cleanup