]> Dogcows Code - chaz/talk-event-driven-programming-in-perl/blob - slides.html
4da6ba0c16f4afec585ea38ac1102fa99c2b8b80
[chaz/talk-event-driven-programming-in-perl] / slides.html
1 <!DOCTYPE html>
2 <html><head><meta charset="utf-8"><title>Event-driven Programming in Perl</title><link rel="stylesheet" href="css/common.css"><link rel="stylesheet" href="css/slides.css"></head><body><textarea id="source">
3
4 class: center, middle
5 name: title
6
7 # Event-driven Programming in Perl
8
9 Charles McGarvey
10
11 ???
12 - Hi. I'm Charles McGarvey.
13 - Been a Perl programmer for a long time...
14
15 ---
16 class: center, middle
17 name: bluehost
18
19 ???
20 - My employer is hiring.
21 - It's a pretty cool employer...
22
23 ---
24 ## "Normal" [userspace] programs
25
26 .big[
27 1. Parse args.
28 2. Read input.
29 3. Do stuff.
30 4. Write results as output.
31 ]
32
33 ---
34 class: pizza
35 background-image: url(img/pizza.jpg)
36 background-size: 100%
37
38 --
39 ```bash
40 # orderpizza --crust-type=thin \
41 --toppings=cheese,pepperoni,mushrooms
42 ```
43
44 --
45 ```perl
46 # source code of the orderpizza program
47
48 my $order = get_order_from_args(@ARGV);
49
50 my $ingredients = gather_ingredients($order);
51
52 my $pizza = prepare_and_bake($order, $ingredentials);
53
54 print($pizza);
55 ```
56
57 ???
58 But some programs are long-lived.
59
60 ---
61 ## Event-driven programs
62
63 .big[
64 1. Events happen.
65 2. When events happen, some code runs and does something.
66 ]
67
68 ???
69 At it's core, event-driven programming is this simple.
70
71 But there are some complications and things to knows, which is why this talk exists.
72
73 ---
74 class: center, middle
75
76 ![Help desk](img/helpdesk.jpg)
77
78 .small[
79 Image by Soniachat8.
80 This image is licensed under the [Creative Commons Attribution-Share Alike 4.0 International](https://creativecommons.org/licenses/by-sa/4.0/deed.en) license.
81 ]
82
83 ---
84 name: graph-eventloop
85 class: center, middle
86
87 ## Event loop
88
89 ![Event loop](img/eventloop.svg)
90
91 ???
92 We'll refine this model as we go.
93
94 ---
95 class: ex-hwinterrupts
96
97 ## Hardware interrupts
98
99 ```bash
100 # cat /proc/interrupts
101 CPU0 CPU1
102 0: 51 0 IR-IO-APIC 2-edge timer
103 1: 685006 5 IR-IO-APIC 1-edge i8042
104 8: 0 0 IR-IO-APIC 8-edge rtc0
105 9: 1724419 6314 IR-IO-APIC 9-fasteoi acpi
106 12: 12300601 138 IR-IO-APIC 12-edge i8042
107 16: 0 0 IR-IO-APIC 16-fasteoi i801_smbus
108 120: 0 0 DMAR-MSI 0-edge dmar0
109 121: 0 0 DMAR-MSI 1-edge dmar1
110 122: 7009890 45112 IR-PCI-MSI 327680-edge xhci_hcd
111 123: 44 3 IR-PCI-MSI 360448-edge mei_me
112 124: 509 0 IR-PCI-MSI 1048576-edge rtsx_pci
113 125: 80130 0 IR-PCI-MSI 2621440-edge nvme0q0, nvme0q1
114 126: 121892439 2961357 IR-PCI-MSI 32768-edge i915
115 127: 49 100 IR-PCI-MSI 514048-edge snd_hda_intel:card0
116 128: 0 79412 IR-PCI-MSI 2621441-edge nvme0q2
117
118 ...
119 ```
120
121 ???
122 When a HW interrupt happens, the appropriate code to run is locate in the interrupt service routine
123
124 ---
125 class: ex-pressenter
126
127 ## Your first event-driven program
128
129 .middle[
130 ```perl
131 print "Press <Enter> to continue.";
132 <STDIN>;
133
134 handle_enter_keypress_event();
135 ```
136 ]
137
138 ---
139 class: ex-pressenter
140
141 ## Your first event-driven program
142
143 .middle[
144 ```perl
145 print "Press <Enter> to continue.";
146 *<STDIN>;
147
148 handle_enter_keypress_event();
149 ```
150 ]
151
152 .big[
153 - Wait for an event
154 ]
155
156 ---
157 class: ex-pressenter
158
159 ## Your first event-driven program
160
161 .middle[
162 ```perl
163 print "Press <Enter> to continue.";
164 *<STDIN>;
165
166 handle_enter_keypress_event();
167 ```
168 ]
169
170 .big[
171 - Wait for an event
172 - Event source
173 ]
174
175 ---
176 class: ex-pressenter
177
178 ## Your first event-driven program
179
180 .middle[
181 ```perl
182 print "Press <Enter> to continue.";
183 <STDIN>;
184
185 *handle_enter_keypress_event();
186 ```
187 ]
188
189 .big[
190 - Wait for an event
191 - Event source
192 - Event handler
193 ]
194
195 ---
196 class: ex-signals
197
198 ## Signals
199
200 ```perl
201 use POSIX;
202
203 $SIG{USR1} = sub { handle_usr1_signal_event() };
204 $SIG{ALRM} = sub { handle_timer_event() };
205 $SIG{QUIT} = sub { exit() };
206
207 alarm(3);
208
209 while (1) { POSIX::pause() }
210 ```
211
212 ---
213 class: ex-signals
214
215 ## Signals
216
217 ```perl
218 use POSIX;
219
220 *$SIG{USR1} = sub { handle_usr1_signal_event() };
221 *$SIG{ALRM} = sub { handle_timer_event() };
222 *$SIG{QUIT} = sub { exit() };
223
224 alarm(3);
225
226 while (1) { POSIX::pause() }
227 ```
228
229 ???
230 Notice that this program is capable of handling more than just one event handler at a time.
231
232 ---
233 class: ex-signals
234
235 ## Signals
236
237 ```perl
238 use POSIX;
239
240 $SIG{USR1} = sub { handle_usr1_signal_event() };
241 $SIG{ALRM} = sub { handle_timer_event() };
242 $SIG{QUIT} = sub { exit() };
243
244 alarm(3);
245
246 *while (1) { POSIX::pause() }
247 ```
248
249 .big[
250 - Program yields time to the kernel
251 ]
252
253 ???
254 This example has a loop, but it's not really doing anything.
255
256 But actually it is doing something very important: It's yielding its processing time to the kernel.
257
258 And kernel informs the program of events.
259
260 You might be able to use `sleep` as well, but some implementations of libc
261 in the past have implemented `sleep` using `alarm`.
262
263 Don't know how common that is nowadays, but old habbits...
264
265 ---
266 class: ex-gui
267
268 ## Graphical user interface example
269
270 ```perl
271 use Gtk3 '-init';
272
273 my $window = Gtk3::Window->new;
274
275 $window->signal_connect('key-press-event' => \&handle_keypress_event);
276 $window->signal_connect('delete-event' => \&Gtk3::main_quit);
277
278 $window->show_all;
279
280 Gtk3::main();
281 ```
282
283 ???
284 A user interface happens to be a good place to use evented code because humans are spontaneous and unpredictable.
285
286 ---
287 class: ex-gui
288
289 ## Graphical user interface example
290
291 ```perl
292 use Gtk3 '-init';
293
294 my $window = Gtk3::Window->new;
295
296 *$window->signal_connect('key-press-event' => \&handle_keypress_event);
297 *$window->signal_connect('delete-event' => \&Gtk3::main_quit);
298
299 $window->show_all;
300
301 Gtk3::main();
302 ```
303
304 .big[
305 - Event handlers
306 ]
307
308 ---
309 class: ex-gui
310
311 ## Graphical user interface example
312
313 ```perl
314 use Gtk3 '-init';
315
316 my $window = Gtk3::Window->new;
317
318 $window->signal_connect('key-press-event' => \&handle_keypress_event);
319 $window->signal_connect('delete-event' => \&Gtk3::main_quit);
320
321 $window->show_all;
322
323 *Gtk3::main();
324 ```
325
326 .big[
327 - Event handlers
328 - Yield and wait for events
329 ]
330
331 ---
332 class: event-types
333
334 ## Types of events
335
336 - IO
337
338 ???
339 Data available on a socket, or a new connection.
340
341 Server or client, data across a wire cannot typically be relied upon to arrive in a predictable fashion, so an
342 event-driven architect makes a lot of sense for network applications.
343
344 --
345 - Signals
346
347 ???
348 As far as my program is concerned, it can receive a signal or message from another program at any time.
349
350 --
351 - Timer
352
353 ???
354 If I need something to happen to happen in five minutes or at a specific absolute time, using the idea of an alarm clock
355 is tempting. I can set an alarm and pretend that the clock itself is a source of events.
356
357 --
358 - User input
359
360 ???
361 Human beings of course are masters of spontaneity.
362
363 Are they going to press a button on the keyboard next, or move the mouse? If my program is connected to a microphone,
364 maybe the human is going to start talking to the program. The program has to be ready for anything, so defining and
365 accepting "events" for all the different ways that a human can interact with the program is a good way to go.
366
367 --
368 - Anything that can happen spontaneously in the real world.
369
370 ???
371 Lots of other external systems (besides humans) can "generate" events.
372
373 Lot of this requires kernel facilities. Speaking of which, how are these types of things implemented?
374
375 ---
376 class: syscalls
377
378 ## How event-driven userspace code works
379
380 ### syscalls
381
382 - [`pause`](http://man.he.net/man2/pause) - Sleeps until signal
383
384 --
385 - [`select`](http://man.he.net/man2/select), [`poll`](http://man.he.net/man2/poll), [`epoll`](http://man.he.net/man7/epoll), [`kqueue`](https://www.freebsd.org/cgi/man.cgi?format=ascii&sektion=2&query=kqueue) - Monitor multiple file descriptors
386
387 --
388 - [`clock_gettime`](http://man.he.net/man2/clock_gettime) - What time is it now?
389
390 ---
391 class: center, middle
392
393 ## Reactor pattern
394
395 ---
396 ## Reactor pattern
397
398 .big[
399 - Queues events asynchronously.
400 - Demultiplexes and dispatches synchronously.
401 ]
402
403 ---
404 name: graph-reactor
405 class: center, middle
406
407 ## Reactor pattern
408
409 ![Reactor](img/reactor.svg)
410
411 ---
412 class: ex-basicreactor1
413
414 ## Using a reactor
415
416 ```perl
417 my $reactor = My::Reactor->new;
418
419 # Set up event handlers
420 $reactor->slurp_file($filepath, \&handle_slurp_event);
421 $reactor->timer(5, \&handle_timer_event);
422 $reactor->listen($socket, \&handle_new_connect_event);
423 ...
424
425 $reactor->run_loop;
426 ```
427
428 ---
429 class: ex-basicreactor2
430
431 ## The basic reactor implementation
432
433 ```perl
434 our $timers = [...];
435 our $io_handles = [...];
436
437 while (1) {
438 my $next_timer = find_next_timer($timers);
439
440 poll($io_handles, $next_timer->time_from_now);
441
442 handle_ready_io_handles($io_handles);
443 handle_expired_timers($timers);
444 }
445 ```
446
447 ---
448 class: ex-basicreactor2
449
450 ## The basic reactor implementation
451
452 ```perl
453 our $timers = [...];
454 our $io_handles = [...];
455
456 while (1) {
457 my $next_timer = find_next_timer($timers);
458
459 * poll($io_handles, $next_timer->time_from_now);
460
461 handle_ready_io_handles($io_handles);
462 handle_expired_timers($timers);
463 }
464 ```
465
466 ---
467 ## Reactor examples on CPAN
468
469 .big[
470 - [`POE::Loop::IO_Poll`](https://metacpan.org/source/POE::Loop::IO_Poll)
471 - [`POE::Loop::Select`](https://metacpan.org/source/POE::Loop::Select)
472 - [`AnyEvent::Loop`](https://metacpan.org/source/AnyEvent::Loop)
473 - [`IO::Async::Loop::Poll`](https://metacpan.org/source/IO::Async::Loop::Poll)
474 - [`IO::Async::Loop::Select`](https://metacpan.org/source/IO::Async::Loop::Select)
475 - [`Mojo::Reactor::Poll`](https://metacpan.org/source/Mojo::Reactor::Poll)
476 ]
477
478 ???
479 These links, which will be available to you with the slides, link directly to the source code of these modules on
480 metacpan so you can take a look at how they work.
481
482 ---
483 ## Reactors
484
485 .col.big[
486 - [`Cocoa::EventLoop`](https://metacpan.org/pod/Cocoa::EventLoop)
487 - [`EV`](https://metacpan.org/pod/EV)
488 - [`Event::Lib`](https://metacpan.org/pod/Event::Lib)
489 - [`Event`](https://metacpan.org/pod/Event)
490 - [`FLTK`](https://metacpan.org/pod/FLTK)
491 ]
492 .col.big[
493 - [`Glib`](https://metacpan.org/pod/Glib), [`Gtk`](https://metacpan.org/pod/Gtk), [`Gtk2`](https://metacpan.org/pod/Gtk2)
494 - [`Tk`](https://metacpan.org/pod/Tk)
495 - [`UV`](https://metacpan.org/pod/UV)
496 - [`Wx`](https://metacpan.org/pod/Wx)
497 - probably more...
498 ]
499
500 ???
501 I'm not going to go over any of these.
502
503 - You can use any one of these directly.
504 - Some of that are better than others (obvious).
505 - Which one you choose may depend one what you're building.
506 - If you're building a GUI, your choice is made for you.
507 - High-concurrency network application, `EV` is a good choice.
508
509 But actually you may not need to pick one...
510
511 ---
512 class: center, middle
513
514 ## Reactor "front ends"
515
516 ???
517 By my count there are four main front ends.
518
519 ---
520 ## Reactor "front ends"
521
522 .big[
523 - [**`POE`**](https://metacpan.org/pod/POE) - Portable multitasking and networking framework for any event loop (or Perl, Objects, and Events)
524 - [**`IO::Async`**](https://metacpan.org/pod/IO::Async) - Asynchronous event-driven programming
525 - [**`Mojo::IOLoop`**](https://metacpan.org/pod/Mojo::IOLoop) - Minimalistic event loop
526 - [**`AnyEvent`**](https://metacpan.org/pod/AnyEvent) - The DBI of event loop programming
527 ]
528
529 ???
530 The benefit of using one of these rather than the reactors themselves is that your code will automatically work with any
531 of a number of supported reactors.
532
533 ---
534 class: ex-poe
535
536 ## `POE`
537
538 ```perl
539 use POE;
540 use Time::HiRes qw(time);
541
542 POE::Session->create(
543 inline_states => {
544 _start => sub {
545 $_[KERNEL]->delay(tick => 5);
546 },
547
548 tick => \&handle_timer_event,
549 },
550 );
551
552 POE::Kernel->run;
553 ```
554
555 ???
556 POE:
557 - The oldest, released 1998
558 - Parallel processing was it's primary use.
559
560 ---
561 class: ex-poe
562
563 ## `POE`
564
565 ```perl
566 *use POE;
567 use Time::HiRes qw(time);
568
569 POE::Session->create(
570 inline_states => {
571 _start => sub {
572 $_[KERNEL]->delay(tick => 5);
573 },
574
575 tick => \&handle_timer_event,
576 },
577 );
578
579 POE::Kernel->run;
580 ```
581
582 ???
583 Using `POE` will implicitly load all the modules you'll need to actually do anything.
584 - In this case, `POE::Session` and `POE::Kernel`.
585
586 ---
587 class: ex-poe
588
589 ## `POE`
590
591 ```perl
592 use POE;
593 use Time::HiRes qw(time);
594
595 POE::Session->create(
596 inline_states => {
597 _start => sub {
598 $_[KERNEL]->delay(tick => 5);
599 },
600
601 tick => \&handle_timer_event,
602 },
603 );
604
605 *POE::Kernel->run;
606 ```
607
608 ???
609 Run the reactor.
610
611 In POE, the kernel is the thing that manages processes AKA sessions.
612
613 ---
614 class: ex-poe
615
616 ## `POE`
617
618 ```perl
619 use POE;
620 use Time::HiRes qw(time);
621
622 *POE::Session->create(
623 * inline_states => {
624 * _start => sub {
625 * $_[KERNEL]->delay(tick => 5);
626 * },
627 *
628 * tick => \&handle_timer_event,
629 * },
630 *);
631
632 POE::Kernel->run;
633 ```
634
635 ???
636
637 Sessions can be created to do stuff.
638
639 ---
640 class: ex-ioasync
641
642 ## `IO::Async`
643
644 ```perl
645 use IO::Async::Loop;
646 use IO::Async::Timer::Countdown;
647
648 my $loop = IO::Async::Loop->new;
649
650 my $timer = IO::Async::Timer::Countdown->new(
651 delay => 5, # seconds
652 on_expire => \&handle_timer_event,
653 );
654 $timer->start;
655
656 $loop->add($timer);
657
658 $loop->run;
659 ```
660
661 ---
662 class: ex-ioasync
663
664 ## `IO::Async`
665
666 ```perl
667 use IO::Async::Loop;
668 use IO::Async::Timer::Countdown;
669
670 *my $loop = IO::Async::Loop->new;
671
672 my $timer = IO::Async::Timer::Countdown->new(
673 delay => 5, # seconds
674 on_expire => \&handle_timer_event,
675 );
676 $timer->start;
677
678 $loop->add($timer);
679
680 $loop->run;
681 ```
682
683 ???
684 Create the loop
685 - IO::Async doesn't seem to have a concept of a "default" loop.
686 - The user has control over starting the loop, as usual, but it does mean that modules can't really register
687 themselves.
688
689 ---
690 class: ex-ioasync
691
692 ## `IO::Async`
693
694 ```perl
695 use IO::Async::Loop;
696 use IO::Async::Timer::Countdown;
697
698 my $loop = IO::Async::Loop->new;
699
700 my $timer = IO::Async::Timer::Countdown->new(
701 delay => 5, # seconds
702 on_expire => \&handle_timer_event,
703 );
704 $timer->start;
705
706 *$loop->add($timer);
707
708 *$loop->run;
709 ```
710
711 ???
712 Add your handler to the loop, and of course run the loop.
713
714 ---
715 class: ex-ioasync
716
717 ## `IO::Async`
718
719 ```perl
720 use IO::Async::Loop;
721 use IO::Async::Timer::Countdown;
722
723 my $loop = IO::Async::Loop->new;
724
725 my $timer = IO::Async::Timer::Countdown->new(
726 delay => 5, # seconds
727 on_expire => \&handle_timer_event,
728 );
729 *$timer->start;
730
731 $loop->add($timer);
732
733 $loop->run;
734 ```
735
736 ???
737 Remember to actually start your timer! The timer facilities in other loops seem to do this for you, but this does give you more control.
738 - For example, you can call `reset` on the timer to set it back it it's initial delay value.
739
740 ---
741 class: ex-mojoioloop
742
743 ## `Mojo::IOLoop`
744
745 ```perl
746 use Mojo::IOLoop;
747
748 Mojo::IOLoop->timer(5 => \&handle_timer_event);
749
750 Mojo::IOLoop->start if ! Mojo::IOLoop->is_running;
751 ```
752
753 ---
754 class: ex-mojoioloop
755
756 ## `Mojo::IOLoop`
757
758 ```perl
759 use Mojo::IOLoop;
760
761 *Mojo::IOLoop->timer(5 => \&handle_timer_event);
762
763 Mojo::IOLoop->start if ! Mojo::IOLoop->is_running;
764 ```
765
766 ???
767 Create the timer... easy.
768
769 ---
770 class: ex-mojoioloop
771
772 ## `Mojo::IOLoop`
773
774 ```perl
775 use Mojo::IOLoop;
776
777 Mojo::IOLoop->timer(5 => \&handle_timer_event);
778
779 *Mojo::IOLoop->start if ! Mojo::IOLoop->is_running;
780 ```
781
782 ???
783 And start the loop. Very easy.
784
785 - `Mojo::IOLoop` provides methods for creating network clients and servers without much code.
786 - Doesn't seem to have support for demultiplexing signals, but if this is important you can probably set that up
787 directly with the `EV` reactor back end.
788
789 ---
790 class: ex-anyevent
791
792 ## `AnyEvent`
793
794 ```perl
795 use EV;
796 use AE;
797
798 my $timer = AE::timer(5, 0 => \&handle_timer_event);
799
800 EV::run();
801 ```
802
803 ---
804 class: ex-anyevent
805
806 ## `AnyEvent`
807
808 ```perl
809 *use EV;
810 use AE;
811
812 my $timer = AE::timer(5, 0 => \&handle_timer_event);
813
814 *EV::run();
815 ```
816
817 ---
818 class: ex-anyevent
819
820 ## `AnyEvent`
821
822 ```perl
823 *use Glib;
824 use AE;
825
826 *my $loop = Glib::MainLoop->new;
827
828 my $timer = AE::timer(5, 0 => \&handle_timer_event);
829
830 *$loop->run;
831 ```
832
833 ---
834 class: ex-anyevent
835
836 ## `AnyEvent`
837
838 ```perl
839 use Glib;
840 use AE;
841
842 my $loop = Glib::MainLoop->new;
843
844 *my $timer = AE::timer(5, 0 => \&handle_timer_event);
845
846 $loop->run;
847 ```
848
849 ???
850 Create your timer.
851
852 By the way, the second argument to `AE::timer` represents the interval if you want a periodic timer.
853
854 - Remember to save the return value of setting up your timer and other handles.
855 - That thing is actually called a "watcher", and the destruction of watcher objects is how event handlers get
856 unregistered.
857
858 `AnyEvent` technically has two APIs. This is the "simplified" API.
859
860 ---
861 class: ex-anyevent
862
863 ## `AnyEvent`
864
865 ```perl
866 use Glib;
867 *use AnyEvent;
868
869 my $loop = Glib::MainLoop->new;
870
871 *my $timer = AnyEvent->timer(
872 * after => 5,
873 * cb => \&handle_timer_event,
874 *);
875
876 $loop->run;
877 ```
878
879 ???
880 This is what the "complicated" API looks like.
881
882 ---
883 name: not-all-roses
884 class: center, middle
885
886 ![Thorns](img/thorn.jpg)
887
888 ## Watch out for the thorns...
889
890 ???
891 There are some special considerations you need to take when writing event-driven code.
892
893 ---
894 class: center, middle
895
896 ## Exceptions for error handling
897
898 ---
899 class: center, middle
900
901 ### Problem: No exception handler up the call stack
902
903 ---
904 class: ex-exceptions
905
906 ## Rule: Don't die/throw in event handlers.
907
908 --
909 ### Error callback pattern
910
911 ```perl
912 do_something_asynchronously(
913 callback => sub { ... },
914 on_error => sub { ... },
915 );
916 ```
917
918 ---
919 class: ex-exceptions
920
921 ## Rule: Don't die/throw in event handlers.
922
923 ### Use promises
924
925 ```perl
926 my $promise = do_something_asynchronously();
927
928 $promise->on_done(sub { ... });
929 $promise->on_fail(sub { ... });
930 ```
931
932 ???
933 More on this later.
934
935 ---
936 class: center, middle
937
938 ## `SIGPIPE`
939
940 ---
941 class: sigpipe
942 ## `SIGPIPE`
943
944 - Sent to your program when it writes to a pipe that was closed.
945
946 --
947 - Default signal handler terminates the program.
948
949 ---
950 class: ex-sigpipe
951
952 ## Solution: Ignore `SIGPIPE`
953
954 ```perl
955 $SIG{PIPE} = 'IGNORE';
956 ```
957
958 ???
959 Some event loops do this for you.
960
961 --
962 .big[
963 Look for `EPIPE` from syscalls (like [`write`](http://man.he.net/man2/write)) instead.
964
965 (You *are* checking return codes from your system calls... right?)
966 ]
967
968 ---
969 class: center, middle
970
971 ## Debugging event-driven code
972
973 ---
974 class: ex-debugging
975
976 ## Debugging event-driven code
977
978 .big[
979 - Print debug info to `STDERR`.
980 ]
981
982 ```bash
983 export PERL_FUTURE_DEBUG=1
984 # and
985 export ANYEVENT_DEBUG=8
986 # or
987 export MOJO_IOLOOP_DEBUG=1
988 # etc.
989 ```
990
991 ---
992 ## Debugging event-driven code
993
994 .big[
995 - Print debug info to `STDERR`.
996 - Check out [`AnyEvent::Debug`](https://metacpan.org/pod/AnyEvent::Debug).
997 ]
998
999 ---
1000 ## Debugging event-driven code
1001
1002 .big[
1003 - Print debug info to `STDERR`.
1004 - Check out [`AnyEvent::Debug`](https://metacpan.org/pod/AnyEvent::Debug).
1005 - Use [`perl5db.pl`](https://metacpan.org/pod/perl5db.pl) and other `Devel::` debuggers.
1006 ]
1007
1008 ???
1009 Traditional debuggers are still useful for event-driven code.
1010 - Be sure to carefully avoid memory leaks -- they are more devastating in long-lived programs which event-driven
1011 programs tend to be.
1012
1013 ---
1014 class: center, middle
1015
1016 ## Promises:
1017 ### Proxy objects for future values
1018
1019 ???
1020 Proxy objects for values that have not yet been retrieved or computed.
1021
1022 - In some cases, promises can provide you an alternative, often more useful interface to callbacks.
1023
1024 ---
1025 class: ex-future
1026
1027 ## Basic usage (using [`Future`](https://metacpan.org/pod/Future))
1028
1029 ```perl
1030 my $future = fetch_remote_file($url);
1031
1032 $future->on_done(sub($filename, $contents) {
1033 print "Fetched $filename\n";
1034 });
1035 ```
1036
1037 ???
1038 The future is an object that stands in for the thing we actually want until the thing we want is available.
1039
1040 --
1041 ```perl
1042 $future->on_fail(sub($error) {
1043 warn $error;
1044 });
1045 ```
1046
1047 ???
1048 If the operation that provides the `Future` isn't able to fulfill its promise to provide us what we want, the `Future`
1049 may be set to a "fail" state.
1050
1051 - For any operation that can fail (and that includes almost everything), it's a good idea to handle errors.
1052
1053 ---
1054 class: ex-future
1055
1056 ## Chaining futures
1057
1058 ```perl
1059 my $future = fetch_remote_file($url)
1060 ->then(sub($filename, $contents) {
1061 my $another_future = write_local_file("/tmp/$filename",
1062 $contents);
1063 return $another_future;
1064 });
1065
1066 $future->on_done(sub($filepath) {
1067 print "Saved file to $filepath\n";
1068 });
1069
1070 $future->on_fail(sub($error) {
1071 warn $error;
1072 });
1073 ```
1074
1075 ---
1076 class: ex-future2
1077
1078 ## Creating futures
1079
1080 ```perl
1081 use AnyEvent::HTTP;
1082 use Future;
1083
1084 sub fetch_remote_file($url) {
1085 my $future = Future->new;
1086
1087 http_get $url => sub($data, $headers) {
1088 if ($headers->{Status} =~ /^[23]/) {
1089 my ($filename) = get_filename($headers);
1090 $future->done($filename, $data);
1091 }
1092 else {
1093 $future->fail("Failed to fetch file: $headers->{Reason}");
1094 }
1095 };
1096
1097 return $future;
1098 }
1099 ```
1100
1101 ---
1102 class: center, middle
1103
1104 Check out [Paul Evan's blog](http://leonerds-code.blogspot.com/2013/12/futures-advent-day-1.html) for more things you can do with `Future`s.
1105
1106 And, of course, [read the pod](https://metacpan.org/pod/Future).
1107
1108 ???
1109 Paul did an advent calendar with short posts detailing the things you can do with `Future`.
1110
1111 - It's really well done.
1112
1113 ---
1114 class: center, middle
1115
1116 ## There's also [`Future::AsyncAwait`](https://metacpan.org/pod/Future::AsyncAwait).
1117
1118 ???
1119 If you have used JavaScript recently, you may have used its "async/await" feature to clean up your non-blocking code.
1120
1121 ---
1122 class: center, middle
1123
1124 ### Yes, Perl can do it, too!
1125
1126 ---
1127 class: ex-asyncawait
1128
1129 ## Without async and await
1130
1131 ```perl
1132 use Future;
1133
1134 sub do_two_things {
1135 return do_first_thing()->then(sub {
1136 my $first = shift;
1137
1138 return do_second_thing($first)->then(sub {
1139 my $second = shift;
1140
1141 return Future->done([$first, $second]);
1142 });
1143 });
1144 }
1145 ```
1146
1147 ---
1148 class: ex-asyncawait
1149
1150 ## With async and await
1151
1152 ```perl
1153 use Future::AsyncAwait;
1154
1155 async sub do_two_things
1156 {
1157 my $first = await do_first_thing();
1158
1159 my $second = await do_second_thing($first);
1160
1161 return [$first, $second];
1162 }
1163 ```
1164
1165 ???
1166 There are caveats: Localized variable assignments don't work, nor anything that has implied local-like behavior.
1167
1168 ---
1169 class: center, middle
1170
1171 ## Finally, there's also [`Future::Utils`](https://metacpan.org/pod/Future::Utils).
1172
1173 ---
1174 class: ex-future2
1175
1176 ## Call
1177
1178 ```perl
1179 my $future = call {
1180 do_stuff();
1181
1182 ...
1183
1184 my $future = ...;
1185 return $future;
1186 };
1187 ```
1188
1189 Any exceptions throw in the code block are caught and become a failed `Future`.
1190
1191 ---
1192 class: ex-future2
1193
1194 ## Loops and stuff
1195
1196 ```perl
1197 use Future::Utils qw(repeat);
1198
1199 my $eventual_future = repeat {
1200 my $trial_future = ...
1201 return $trial_future;
1202 } while => sub($last_trial_future) { return should_keep_going($last_trial_future) };
1203 ```
1204
1205 ---
1206 ## Events in the world
1207
1208 - Interconnected devices
1209
1210 ???
1211 - Events are everywhere in the world, and our devices can tell other devices about them.
1212 - Interconnected devices (IoT)
1213
1214 ---
1215 class: center, middle
1216 name: conclusion
1217
1218 ## Conclusion:
1219
1220 ### Perl is fun.
1221
1222 ---
1223 class: center, middle
1224 name: last
1225
1226 ### Thanks.
1227
1228 </textarea><script src="https://gnab.github.io/remark/downloads/remark-latest.min.js"></script><script>var slideshow = remark.create({countIncrementalSlides: true, highlightLanguage: '', highlightLines: true, highlightStyle: 'hybrid', ratio: '16:9', /*slideNumberFormat: '',*/ navigation: {scroll: false, touch: false, click: false}})</script><script src="js/common.js"></script><script src="js/slides.js"></script></body></html>
1229 <!-- vim: set ts=4 sts=4 sw=4 tw=120 et ft=markdown nowrap: -->
This page took 0.088734 seconds and 3 git commands to generate.