]> Dogcows Code - chaz/talk-event-driven-programming-in-perl/blobdiff - slides.html
add final slides before presentation
[chaz/talk-event-driven-programming-in-perl] / slides.html
index 46dbe4085e44d73b2f1a40af890e2973997127c1..745bf127db87dedbe3ae370155dfe02d9c935c1b 100644 (file)
@@ -16,13 +16,1253 @@ Charles McGarvey
 class:  center, middle
 name:   bluehost
 
-![Bluehost](img/bluehost.png)
+???
+- My employer is hiring.
+- It's a pretty cool employer...
+
+---
+## "Normal" [userspace] programs
+
+.big[
+1. Parse args.
+2. Read input.
+3. Do stuff.
+4. Write results as output.
+]
+
+---
+class: pizza
+background-image: url(img/pizza.jpg)
+background-size: 100%
+
+--
+```bash
+# orderpizza --crust-type=thin \
+             --toppings=cheese,pepperoni,mushrooms
+```
+
+--
+```perl
+# source code of the orderpizza program
+
+my $order = get_order_from_args(@ARGV);
+
+my $ingredients = gather_ingredients($order);
+
+my $pizza = prepare_and_bake($order, $ingredentials);
+
+print($pizza);
+```
+
+???
+But some programs are long-lived.
+
+---
+## Event-driven programs
+
+.big[
+1. Events happen.
+2. When events happen, some code runs and does something.
+]
+
+???
+At it's core, event-driven programming is this simple.
+
+But there are some complications and things to knows, which is why this talk exists.
+
+---
+class: center, middle
+
+![Help desk](img/helpdesk.jpg)
+
+.small[
+Image by Soniachat8.
+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.
+]
+
+---
+name:  graph-eventloop
+class: center, middle
+
+## Event loop
+
+![Event loop](img/eventloop.svg)
+
+???
+We'll refine this model as we go.
+
+---
+class: ex-hwinterrupts
+
+## Hardware interrupts
+
+```bash
+# cat /proc/interrupts
+            CPU0       CPU1
+   0:         51          0  IR-IO-APIC    2-edge      timer
+   1:     685006          5  IR-IO-APIC    1-edge      i8042
+   8:          0          0  IR-IO-APIC    8-edge      rtc0
+   9:    1724419       6314  IR-IO-APIC    9-fasteoi   acpi
+  12:   12300601        138  IR-IO-APIC   12-edge      i8042
+  16:          0          0  IR-IO-APIC   16-fasteoi   i801_smbus
+ 120:          0          0  DMAR-MSI    0-edge      dmar0
+ 121:          0          0  DMAR-MSI    1-edge      dmar1
+ 122:    7009890      45112  IR-PCI-MSI 327680-edge      xhci_hcd
+ 123:         44          3  IR-PCI-MSI 360448-edge      mei_me
+ 124:        509          0  IR-PCI-MSI 1048576-edge      rtsx_pci
+ 125:      80130          0  IR-PCI-MSI 2621440-edge      nvme0q0, nvme0q1
+ 126:  121892439    2961357  IR-PCI-MSI 32768-edge      i915
+ 127:         49        100  IR-PCI-MSI 514048-edge      snd_hda_intel:card0
+ 128:          0      79412  IR-PCI-MSI 2621441-edge      nvme0q2
+
+...
+```
+
+???
+When a HW interrupt happens, the appropriate code to run is locate in the interrupt service routine
+
+---
+class: ex-pressenter
+
+## Your first event-driven program
+
+.middle[
+```perl
+print "Press <Enter> to continue.";
+<STDIN>;
+
+handle_enter_keypress_event();
+```
+]
+
+---
+class: ex-pressenter
+
+## Your first event-driven program
+
+.middle[
+```perl
+print "Press <Enter> to continue.";
+*<STDIN>;
+
+handle_enter_keypress_event();
+```
+]
+
+.big[
+- Wait for an event
+]
+
+---
+class: ex-pressenter
+
+## Your first event-driven program
+
+.middle[
+```perl
+print "Press <Enter> to continue.";
+*<STDIN>;
+
+handle_enter_keypress_event();
+```
+]
+
+.big[
+- Wait for an event
+- Event source
+]
+
+---
+class: ex-pressenter
+
+## Your first event-driven program
+
+.middle[
+```perl
+print "Press <Enter> to continue.";
+<STDIN>;
+
+*handle_enter_keypress_event();
+```
+]
+
+.big[
+- Wait for an event
+- Event source
+- Event handler
+]
+
+---
+class: ex-signals
+
+## Signals
+
+```perl
+use POSIX;
+
+$SIG{USR1} = sub { handle_usr1_signal_event() };
+$SIG{ALRM} = sub { handle_timer_event() };
+$SIG{QUIT} = sub { exit() };
+
+alarm(3);
+
+while (1) { POSIX::pause() }
+```
+
+---
+class: ex-signals
+
+## Signals
+
+```perl
+use POSIX;
+
+*$SIG{USR1} = sub { handle_usr1_signal_event() };
+*$SIG{ALRM} = sub { handle_timer_event() };
+*$SIG{QUIT} = sub { exit() };
+
+alarm(3);
+
+while (1) { POSIX::pause() }
+```
+
+???
+Notice that this program is capable of handling more than just one event handler at a time.
+
+---
+class: ex-signals
+
+## Signals
+
+```perl
+use POSIX;
+
+$SIG{USR1} = sub { handle_usr1_signal_event() };
+$SIG{ALRM} = sub { handle_timer_event() };
+$SIG{QUIT} = sub { exit() };
+
+alarm(3);
+
+*while (1) { POSIX::pause() }
+```
+
+.big[
+- Program yields time to the kernel
+]
+
+???
+This example has a loop, but it's not really doing anything.
+
+But actually it is doing something very important: It's yielding its processing time to the kernel.
+
+And kernel informs the program of events.
+
+You might be able to use `sleep` as well, but some implementations of libc
+in the past have implemented `sleep` using `alarm`.
+
+Don't know how common that is nowadays, but old habbits...
+
+---
+class: ex-gui
+
+## Graphical user interface example
+
+```perl
+use Gtk3 '-init';
+
+my $window = Gtk3::Window->new;
+
+$window->signal_connect('key-press-event' => \&handle_keypress_event);
+$window->signal_connect('delete-event'    => \&Gtk3::main_quit);
+
+$window->show_all;
+
+Gtk3::main();
+```
+
+???
+A user interface happens to be a good place to use evented code because humans are spontaneous and unpredictable.
+
+---
+class: ex-gui
+
+## Graphical user interface example
+
+```perl
+use Gtk3 '-init';
+
+my $window = Gtk3::Window->new;
+
+*$window->signal_connect('key-press-event' => \&handle_keypress_event);
+*$window->signal_connect('delete-event'    => \&Gtk3::main_quit);
+
+$window->show_all;
+
+Gtk3::main();
+```
+
+.big[
+- Event handlers
+]
+
+---
+class: ex-gui
+
+## Graphical user interface example
+
+```perl
+use Gtk3 '-init';
+
+my $window = Gtk3::Window->new;
+
+$window->signal_connect('key-press-event' => \&handle_keypress_event);
+$window->signal_connect('delete-event'    => \&Gtk3::main_quit);
+
+$window->show_all;
+
+*Gtk3::main();
+```
+
+.big[
+- Event handlers
+- Yield and wait for events
+]
+
+---
+class: event-types
+
+## Types of events
+
+- IO
+
+???
+Data available on a socket, or a new connection.
+
+Server or client, data across a wire cannot typically be relied upon to arrive in a predictable fashion, so an
+event-driven architect makes a lot of sense for network applications.
+
+--
+- Signals
+
+???
+As far as my program is concerned, it can receive a signal or message from another program at any time.
+
+--
+- Timer
+
+???
+If I need something to happen to happen in five minutes or at a specific absolute time, using the idea of an alarm clock
+is tempting. I can set an alarm and pretend that the clock itself is a source of events.
+
+--
+- User input
+
+???
+Human beings of course are masters of spontaneity.
+
+Are they going to press a button on the keyboard next, or move the mouse? If my program is connected to a microphone,
+maybe the human is going to start talking to the program. The program has to be ready for anything, so defining and
+accepting "events" for all the different ways that a human can interact with the program is a good way to go.
+
+--
+- Anything that can happen spontaneously in the real world.
+
+???
+Lots of other external systems (besides humans) can "generate" events.
+
+Lot of this requires kernel facilities. Speaking of which, how are these types of things implemented?
+
+---
+class: syscalls
+
+## How event-driven userspace code works
+
+### syscalls
+
+- [`pause`](http://man.he.net/man2/pause) - Sleeps until signal
+
+--
+- [`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
+
+--
+- [`clock_gettime`](http://man.he.net/man2/clock_gettime) - What time is it now?
+
+---
+class: center, middle
+
+## Reactor pattern
+
+---
+## Reactor pattern
+
+.big[
+- Queues events asynchronously.
+- Demultiplexes and dispatches synchronously.
+]
+
+---
+name:  graph-reactor
+class: center, middle
+
+## Reactor pattern
+
+![Reactor](img/reactor.svg)
+
+---
+class: ex-basicreactor1
+
+## Using a reactor
+
+```perl
+my $reactor = My::Reactor->new;
+
+# Set up event handlers
+$reactor->slurp_file($filepath, \&handle_slurp_event);
+$reactor->timer(5, \&handle_timer_event);
+$reactor->listen($socket, \&handle_new_connect_event);
+...
+
+$reactor->run_loop;
+```
+
+---
+class: ex-basicreactor2
+
+## The basic reactor implementation
+
+```perl
+our $timers     = [...];
+our $io_handles = [...];
+
+while (1) {
+    my $next_timer = find_next_timer($timers);
+
+    poll($io_handles, $next_timer->time_from_now);
+
+    handle_ready_io_handles($io_handles);
+    handle_expired_timers($timers);
+}
+```
+
+???
+Of course, you don't actually need to know anything about which syscalls are used and how a reactor actually works to do
+event-driven programming. (and if any of this is going over your head, that's fine.)
+
+But I'm covering it because I think it's good for you.
+
+---
+class: ex-basicreactor2
+
+## The basic reactor implementation
+
+```perl
+our $timers     = [...];
+our $io_handles = [...];
+
+while (1) {
+    my $next_timer = find_next_timer($timers);
+
+*   poll($io_handles, $next_timer->time_from_now);
+
+    handle_ready_io_handles($io_handles);
+    handle_expired_timers($timers);
+}
+```
+
+---
+## Reactor examples on CPAN
+
+.big[
+- [`POE::Loop::IO_Poll`](https://metacpan.org/source/POE::Loop::IO_Poll)
+- [`POE::Loop::Select`](https://metacpan.org/source/POE::Loop::Select)
+- [`AnyEvent::Loop`](https://metacpan.org/source/AnyEvent::Loop)
+- [`IO::Async::Loop::Poll`](https://metacpan.org/source/IO::Async::Loop::Poll)
+- [`IO::Async::Loop::Select`](https://metacpan.org/source/IO::Async::Loop::Select)
+- [`Mojo::Reactor::Poll`](https://metacpan.org/source/Mojo::Reactor::Poll)
+]
+
+???
+These links, which will be available to you with the slides, link directly to the source code of these modules on
+metacpan so you can take a look at how they work.
+
+---
+## Reactors
+
+.col.big[
+- [`Cocoa::EventLoop`](https://metacpan.org/pod/Cocoa::EventLoop)
+- [`EV`](https://metacpan.org/pod/EV)
+- [`Event::Lib`](https://metacpan.org/pod/Event::Lib)
+- [`Event`](https://metacpan.org/pod/Event)
+- [`FLTK`](https://metacpan.org/pod/FLTK)
+]
+.col.big[
+- [`Glib`](https://metacpan.org/pod/Glib), [`Gtk`](https://metacpan.org/pod/Gtk), [`Gtk2`](https://metacpan.org/pod/Gtk2)
+- [`Tk`](https://metacpan.org/pod/Tk)
+- [`UV`](https://metacpan.org/pod/UV)
+- [`Wx`](https://metacpan.org/pod/Wx)
+- probably more...
+]
+
+???
+I'm not going to go over any of these.
+
+- You can use any one of these directly.
+- Some of that are better than others (obvious).
+- Which one you choose may depend one what you're building.
+  - If you're building a GUI, your choice is made for you.
+  - High-concurrency network application, `EV` is a good choice.
+
+But actually you may not need to pick one...
+
+---
+class: center, middle
+
+## Reactor "front ends"
+
+???
+By my count there are four main front ends.
+
+---
+## Reactor "front ends"
+
+.big[
+- [**`POE`**](https://metacpan.org/pod/POE) - Portable multitasking and networking framework for any event loop (or Perl, Objects, and Events)
+- [**`IO::Async`**](https://metacpan.org/pod/IO::Async) - Asynchronous event-driven programming
+- [**`Mojo::IOLoop`**](https://metacpan.org/pod/Mojo::IOLoop) - Minimalistic event loop
+- [**`AnyEvent`**](https://metacpan.org/pod/AnyEvent) - The DBI of event loop programming
+]
+
+???
+The benefit of using one of these rather than the reactors themselves is that your code will automatically work with any
+of a number of supported reactors.
+
+---
+class: ex-poe
+
+## `POE`
+
+```perl
+use POE;
+use Time::HiRes qw(time);
+
+POE::Session->create(
+    inline_states => {
+        _start  => sub {
+            $_[KERNEL]->delay(tick => 5);
+        },
+
+        tick    => \&handle_timer_event,
+    },
+);
+
+POE::Kernel->run;
+```
+
+???
+POE:
+- The oldest, released 1998
+- Parallel processing was it's primary use.
+
+---
+class: ex-poe
+
+## `POE`
+
+```perl
+*use POE;
+use Time::HiRes qw(time);
+
+POE::Session->create(
+    inline_states => {
+        _start  => sub {
+            $_[KERNEL]->delay(tick => 5);
+        },
+
+        tick    => \&handle_timer_event,
+    },
+);
+
+POE::Kernel->run;
+```
+
+???
+Using `POE` will implicitly load all the modules you'll need to actually do anything.
+- In this case, `POE::Session` and `POE::Kernel`.
+
+---
+class: ex-poe
+
+## `POE`
+
+```perl
+use POE;
+use Time::HiRes qw(time);
+
+POE::Session->create(
+    inline_states => {
+        _start  => sub {
+            $_[KERNEL]->delay(tick => 5);
+        },
+
+        tick    => \&handle_timer_event,
+    },
+);
+
+*POE::Kernel->run;
+```
+
+???
+Run the reactor.
+
+In POE, the kernel is the thing that manages processes AKA sessions.
+
+---
+class: ex-poe
+
+## `POE`
+
+```perl
+use POE;
+use Time::HiRes qw(time);
+
+*POE::Session->create(
+*   inline_states => {
+*       _start  => sub {
+*           $_[KERNEL]->delay(tick => 5);
+*       },
+*
+*       tick    => \&handle_timer_event,
+*   },
+*);
+
+POE::Kernel->run;
+```
+
+???
+
+Sessions can be created to do stuff.
+
+---
+class: ex-ioasync
+
+## `IO::Async`
+
+```perl
+use IO::Async::Loop;
+use IO::Async::Timer::Countdown;
+
+my $loop = IO::Async::Loop->new;
+
+my $timer = IO::Async::Timer::Countdown->new(
+    delay       => 5,  # seconds
+    on_expire   => \&handle_timer_event,
+);
+$timer->start;
+
+$loop->add($timer);
+
+$loop->run;
+```
+
+---
+class: ex-ioasync
+
+## `IO::Async`
+
+```perl
+use IO::Async::Loop;
+use IO::Async::Timer::Countdown;
+
+*my $loop = IO::Async::Loop->new;
+
+my $timer = IO::Async::Timer::Countdown->new(
+    delay       => 5,  # seconds
+    on_expire   => \&handle_timer_event,
+);
+$timer->start;
+
+$loop->add($timer);
+
+$loop->run;
+```
+
+???
+Create the loop
+- IO::Async doesn't seem to have a concept of a "default" loop.
+  - The user has control over starting the loop, as usual, but it does mean that modules can't really register
+    themselves.
 
-### https://bluehost.com/jobs
+---
+class: ex-ioasync
+
+## `IO::Async`
+
+```perl
+use IO::Async::Loop;
+use IO::Async::Timer::Countdown;
+
+my $loop = IO::Async::Loop->new;
+
+my $timer = IO::Async::Timer::Countdown->new(
+    delay       => 5,  # seconds
+    on_expire   => \&handle_timer_event,
+);
+$timer->start;
+
+*$loop->add($timer);
+
+*$loop->run;
+```
 
 ???
-- My employer is hiring.
-- It's a pretty cool employer...
+Add your handler to the loop, and of course run the loop.
+
+---
+class: ex-ioasync
+
+## `IO::Async`
+
+```perl
+use IO::Async::Loop;
+use IO::Async::Timer::Countdown;
+
+my $loop = IO::Async::Loop->new;
+
+my $timer = IO::Async::Timer::Countdown->new(
+    delay       => 5,  # seconds
+    on_expire   => \&handle_timer_event,
+);
+*$timer->start;
+
+$loop->add($timer);
+
+$loop->run;
+```
+
+???
+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.
+- For example, you can call `reset` on the timer to set it back it it's initial delay value.
+
+---
+class: ex-mojoioloop
+
+## `Mojo::IOLoop`
+
+```perl
+use Mojo::IOLoop;
+
+Mojo::IOLoop->timer(5 => \&handle_timer_event);
+
+Mojo::IOLoop->start if ! Mojo::IOLoop->is_running;
+```
+
+---
+class: ex-mojoioloop
+
+## `Mojo::IOLoop`
+
+```perl
+use Mojo::IOLoop;
+
+*Mojo::IOLoop->timer(5 => \&handle_timer_event);
+
+Mojo::IOLoop->start if ! Mojo::IOLoop->is_running;
+```
+
+???
+Create the timer... easy.
+
+---
+class: ex-mojoioloop
+
+## `Mojo::IOLoop`
+
+```perl
+use Mojo::IOLoop;
+
+Mojo::IOLoop->timer(5 => \&handle_timer_event);
+
+*Mojo::IOLoop->start if ! Mojo::IOLoop->is_running;
+```
+
+???
+And start the loop. Very easy.
+
+- `Mojo::IOLoop` provides methods for creating network clients and servers without much code.
+- Doesn't seem to have support for demultiplexing signals, but if this is important you can probably set that up
+  directly with the `EV` reactor back end.
+
+---
+class: ex-anyevent
+
+## `AnyEvent`
+
+```perl
+use EV;
+use AE;
+
+my $timer = AE::timer(5, 0 => \&handle_timer_event);
+
+EV::run();
+```
+
+---
+class: ex-anyevent
+
+## `AnyEvent`
+
+```perl
+*use EV;
+use AE;
+
+my $timer = AE::timer(5, 0 => \&handle_timer_event);
+
+*EV::run();
+```
+
+---
+class: ex-anyevent
+
+## `AnyEvent`
+
+```perl
+*use Glib;
+use AE;
+
+*my $loop = Glib::MainLoop->new;
+
+my $timer = AE::timer(5, 0 => \&handle_timer_event);
+
+*$loop->run;
+```
+
+---
+class: ex-anyevent
+
+## `AnyEvent`
+
+```perl
+use Glib;
+use AE;
+
+my $loop = Glib::MainLoop->new;
+
+*my $timer = AE::timer(5, 0 => \&handle_timer_event);
+
+$loop->run;
+```
+
+???
+Create your timer.
+
+By the way, the second argument to `AE::timer` represents the interval if you want a periodic timer.
+
+- Remember to save the return value of setting up your timer and other handles.
+  - That thing is actually called a "watcher", and the destruction of watcher objects is how event handlers get
+    unregistered.
+
+`AnyEvent` technically has two APIs. This is the "simplified" API.
+
+---
+class: ex-anyevent
+
+## `AnyEvent`
+
+```perl
+use Glib;
+*use AnyEvent;
+
+my $loop = Glib::MainLoop->new;
+
+*my $timer = AnyEvent->timer(
+*   after   => 5,
+*   cb      => \&handle_timer_event,
+*);
+
+$loop->run;
+```
+
+???
+This is what the "complicated" API looks like.
+
+---
+name:  not-all-roses
+class: center, middle
+
+![Thorns](img/thorn.jpg)
+
+## Watch out for the thorns...
+
+???
+There are some special considerations you need to take when writing event-driven code.
+
+---
+class: center, middle
+
+## Exceptions for error handling
+
+---
+class: center, middle
+
+### Problem: No exception handler up the call stack
+
+---
+class: ex-exceptions
+
+## Rule: Don't die/throw in event handlers.
+
+--
+### Error callback pattern
+
+```perl
+do_something_asynchronously(
+    callback => sub { ... },
+    on_error => sub { ... },
+);
+```
+
+---
+class: ex-exceptions
+
+## Rule: Don't die/throw in event handlers.
+
+### Use promises
+
+```perl
+my $promise = do_something_asynchronously();
+
+$promise->on_done(sub { ... });
+$promise->on_fail(sub { ... });
+```
+
+???
+More on this later.
+
+---
+class: center, middle
+
+## `SIGPIPE`
+
+---
+class: sigpipe
+## `SIGPIPE`
+
+- Sent to your program when it writes to a pipe that was closed.
+
+--
+- Default signal handler terminates the program.
+
+---
+class: ex-sigpipe
+
+## Solution: Ignore `SIGPIPE`
+
+```perl
+$SIG{PIPE} = 'IGNORE';
+```
+
+???
+Some event loops do this for you.
+
+--
+.big[
+Look for `EPIPE` from syscalls (like [`write`](http://man.he.net/man2/write)) instead.
+
+(You *are* checking return codes from your system calls... right?)
+]
+
+---
+class: center, middle
+
+## Debugging event-driven code
+
+---
+class: ex-debugging
+
+## Debugging event-driven code
+
+.big[
+- Print debug info to `STDERR`.
+]
+
+```bash
+export PERL_FUTURE_DEBUG=1
+# and
+export ANYEVENT_DEBUG=8
+# or
+export MOJO_IOLOOP_DEBUG=1
+# etc.
+```
+
+---
+## Debugging event-driven code
+
+.big[
+- Print debug info to `STDERR`.
+- Check out [`AnyEvent::Debug`](https://metacpan.org/pod/AnyEvent::Debug).
+]
+
+---
+## Debugging event-driven code
+
+.big[
+- Print debug info to `STDERR`.
+- Check out [`AnyEvent::Debug`](https://metacpan.org/pod/AnyEvent::Debug).
+- Use [`perl5db.pl`](https://metacpan.org/pod/perl5db.pl) and other `Devel::` debuggers.
+]
+
+???
+Traditional debuggers are still useful for event-driven code.
+- Be sure to carefully avoid memory leaks -- they are more devastating in long-lived programs which event-driven
+programs tend to be.
+
+---
+class: center, middle
+
+## Promises:
+### Proxy objects for future values
+
+???
+Proxy objects for values that have not yet been retrieved or computed.
+
+- In some cases, promises can provide you an alternative, often more useful interface to callbacks.
+
+---
+class: ex-future
+
+## Basic usage (using [`Future`](https://metacpan.org/pod/Future))
+
+```perl
+my $future = fetch_remote_file($url);
+
+$future->on_done(sub($filename, $contents) {
+    print "Fetched $filename\n";
+});
+```
+
+???
+The future is an object that stands in for the thing we actually want until the thing we want is available.
+
+--
+```perl
+$future->on_fail(sub($error) {
+    warn $error;
+});
+```
+
+???
+If the operation that provides the `Future` isn't able to fulfill its promise to provide us what we want, the `Future`
+may be set to a "fail" state.
+
+- For any operation that can fail (and that includes almost everything), it's a good idea to handle errors.
+
+---
+class: ex-future
+
+## Chaining futures
+
+```perl
+my $future = fetch_remote_file($url)
+    ->then(sub($filename, $contents) {
+        my $another_future = write_local_file("/tmp/$filename",
+                                              $contents);
+        return $another_future;
+    });
+
+$future->on_done(sub($filepath) {
+    print "Saved file to $filepath\n";
+});
+
+$future->on_fail(sub($error) {
+    warn $error;
+});
+```
+
+---
+class: ex-future2
+
+## Creating futures
+
+```perl
+use AnyEvent::HTTP;
+use Future;
+
+sub fetch_remote_file($url) {
+    my $future = Future->new;
+
+    http_get $url => sub($data, $headers) {
+        if ($headers->{Status} =~ /^[23]/) {
+            my ($filename) = get_filename($headers);
+            $future->done($filename, $data);
+        }
+        else {
+            $future->fail("Failed to fetch file: $headers->{Reason}");
+        }
+    };
+
+    return $future;
+}
+```
+
+---
+class: center, middle
+
+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.
+
+And, of course, [read the pod](https://metacpan.org/pod/Future).
+
+???
+Paul did an advent calendar with short posts detailing the things you can do with `Future`.
+
+- It's really well done.
+
+---
+class: center, middle
+
+## There's also [`Future::AsyncAwait`](https://metacpan.org/pod/Future::AsyncAwait).
+
+???
+If you have used JavaScript recently, you may have used its "async/await" feature to clean up your non-blocking code.
+
+---
+class: center, middle
+
+### Yes, Perl can do it, too!
+
+---
+class: ex-asyncawait
+
+## Without async and await
+
+```perl
+use Future;
+
+sub do_two_things {
+    return do_first_thing()->then(sub {
+        my $first = shift;
+
+        return do_second_thing($first)->then(sub {
+            my $second = shift;
+
+            return Future->done([$first, $second]);
+        });
+    });
+}
+```
+
+---
+class: ex-asyncawait
+
+## With async and await
+
+```perl
+use Future::AsyncAwait;
+
+async sub do_two_things
+{
+    my $first = await do_first_thing();
+
+    my $second = await do_second_thing($first);
+
+    return [$first, $second];
+}
+```
+
+???
+There are caveats: Localized variable assignments don't work, nor anything that has implied local-like behavior.
+
+---
+class: center, middle
+
+## Finally, there's also [`Future::Utils`](https://metacpan.org/pod/Future::Utils).
+
+---
+class: ex-future2
+
+## Call
+
+```perl
+my $future = call {
+    do_stuff();
+
+    ...
+
+    my $future = ...;
+    return $future;
+};
+```
+
+Any exceptions throw in the code block are caught and become a failed `Future`.
+
+---
+class: ex-future2
+
+## Loops and stuff
+
+```perl
+use Future::Utils qw(repeat);
+
+my $eventual_future = repeat {
+   my $trial_future = ...
+   return $trial_future;
+} while => sub($last_trial_future) { return should_keep_going($last_trial_future) };
+```
+
+---
+class: center, middle
+
+## Final thoughts
+
+---
+class: center, middle
+
+### Proactor pattern
+
+???
+We've gone over the Reactor pattern quite a bit, and for good reason.
+
+It's the predominant implementation of event-driven code in userspace apps.
+
+But you should also know about the Proactor pattern.
+
+It's basically the same as the reactor pattern except the event handlers perform asynchronous operations.
+- Can be done using special kernel facilities
+- or by keeping a thread pool.
+
+One thing to keep in mind about the reactor pattern is that any sort of blocking that occurs by any event handlers will
+slow everything down quite a bit. The proactor pattern can help avoid problems.
+
+---
+class: center, middle
+
+### Event-driven <strike>programming</strike> architecture
+
+???
+Making your apps reactive and able to generate and respond to events is the tip of a very large iceburg.
+
+- Pubsub systems can be used to distribute events at a massive scale.
+- Message queues are components that can be plugged in to provide guaranteed delivery of events.
+
+Once your programs are event-driven, they're ready to be plugged into a whole world of other services.
+
+---
+class: center, middle
+
+### Lots of interconnected devices
+
+???
+This concept has a trendy name, but I can't say it because I've swarn off buzzwords.
+
+Event-driven programming and architecture is used as a foundation for building APIs and applications that scale, if
+that's important to you.
+
+You can avoid coupling: When one of your devices has an event, it just needs to notify your world of devices and let the
+devices decide what to do.
+
+For example, if your car has connected sensors, it can notify your devices when you leave the car. Then your phone can
+receive that event and notify you that you left your kid in the car.
+
+So there are a lot of cool applications for this stuff.
 
 ---
 class: center, middle
@@ -38,5 +1278,5 @@ name:  last
 
 ### Thanks.
 
-</textarea><script src="https://gnab.github.io/remark/downloads/remark-latest.min.js"></script><script>var slideshow = remark.create({countIncrementalSlides: true, highlightLanguage: '', highlightLines: true, 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>
+</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>
 <!-- vim: set ts=4 sts=4 sw=4 tw=120 et ft=markdown nowrap: -->
This page took 0.04282 seconds and 4 git commands to generate.