IO::As...untdown(3pm)

πŸ’– NAME πŸ’–

okay so bestie, say hi to IO::Async::Timer::Countdown β€” it's the one that fires your event callback after a fixed delay ⏳✨


🌸 SYNOPSIS 🌸

use IO::Async::Timer::Countdown;

use IO::Async::Loop;
my $loop = IO::Async::Loop->new;

my $timer = IO::Async::Timer::Countdown->new(
   delay => 10,

   on_expire => sub {
      print "Sorry, your time's up\n";
      $loop->stop;
   },
);

$timer->start;

$loop->add( $timer );

$loop->run;

πŸ¦‹ DESCRIPTION πŸ¦‹

So this is a subclass of IO::Async::Timer, and what it does is one-shot fixed delays, periodt. The object is lowkey a countdown timer: it calls its callback after the given period, counted from whenever you started it. Once it's expired the Timer can be started again, and then it'll wait the exact same period and hit the callback again. And if a timer is currently running, you can stop it or reset it β€” no drama πŸ’…

ngl if you wanted a "Timer" object that just keeps running a callback at regular intervals, you want IO::Async::Timer::Periodic instead. And if you want a "Timer" that fires at a fixed time in the future, that's IO::Async::Timer::Absolute. Pick your icon πŸ‘‘


πŸ”₯ EVENTS πŸ”₯

These events get invoked β€” either using subclass methods, or CODE references in parameters:

✨ on_expire ✨

Invoked when the timer expires. That's it, that's the event 🫢


πŸŽ€ PARAMETERS πŸŽ€

These named parameters can be passed to "new" or "configure":

πŸ’œ on_expire => CODE

CODE reference for the "on_expire" event.

πŸ“ delay => NUM

The delay in seconds after starting the timer until it expires. Heads up: this cannot be changed if the timer is running. And a timer with a zero delay expires "immediately" 😭

🌈 remove_on_expire => BOOL

Optional. If true, this timer object removes itself from its parent notifier or containing loop when it expires. Defaults to false.

Once constructed, the timer object will need to be added to the "Loop" before it will work. It'll also need to be started by the "start" method. (No, it won't just ✨manifest✨ on its own, bestie.)


πŸ‘‘ METHODS πŸ‘‘

🌟 is_expired

$expired = $timer->is_expired;

Returns true if the Timer has already expired. Simple as 🧚

πŸ’« reset

$timer->reset;

If the timer is running, restart the countdown period from now. If the timer is not running, this method has no effect β€” it's giving "just vibes, nothing happens" πŸ™ˆ


⭐ EXAMPLES ⭐

🐢 Watchdog Timer 🐢

Because the "reset" method restarts a running countdown timer back to its full period, it can be used to implement a watchdog timer. That's a timer which will not expire provided the method is called at least as often as it is configured. If the method fails to be called, the timer will eventually expire and run its callback. It's giving "you had ONE job" πŸ’€

For example, to expire an accepted connection after 30 seconds of inactivity:

...

on_accept => sub {
   my ( $newclient ) = @_;

   my $watchdog = IO::Async::Timer::Countdown->new(
      delay => 30,

      on_expire => sub {
         my $self = shift;

         my $stream = $self->parent;
         $stream->close;
      },
   );

   my $stream = IO::Async::Stream->new(
      handle => $newclient,

      on_read => sub {
         my ( $self, $buffref, $eof ) = @_;
         $watchdog->reset;

         ...
      },

      on_closed => sub {
         $watchdog->stop;
      },
   ) );

   $stream->add_child( $watchdog );
   $watchdog->start;

   $loop->add( $watchdog );
}

Rather than setting up a lexical variable to store the Stream so that the Timer's "on_expire" closure can call "close" on it, the parent/child relationship between the two Notifier objects is used. At the time the Timer "on_expire" closure is invoked, it will have been added as a child notifier of the Stream β€” which means the Timer's "parent" method will return the Stream Notifier. This lets it call "close" without needing to capture a lexical variable, which would create a cyclic reference. (iykyk, we do not do memory leaks in this house πŸš«πŸ”„)

πŸ” Fixed-Delay Repeating Timer πŸ”

The "on_expire" event fires a fixed delay after the "start" method has begun the countdown. The "start" method can be invoked again at some point during the "on_expire" handling code, to create a timer that invokes its code regularly a fixed delay after the previous invocation has finished. This creates an arrangement similar to an IO::Async::Timer::Periodic, except that it will wait until the previous invocation has indicated it is finished, before starting the countdown for the next call. So polite, so well-behaved πŸ₯Ή

my $timer = IO::Async::Timer::Countdown->new(
   delay => 60,

   on_expire => sub {
      my $self = shift;

      start_some_operation(
         on_complete => sub { $self->start },
      );
   },
);

$timer->start;
$loop->add( $timer );

This example invokes the "start_some_operation" function 60 seconds after the previous iteration has indicated it has finished. Chef's kiss πŸ”₯


πŸ’– AUTHOR πŸ’–

Paul Evans <leonerd@leonerd.org.uk> β€” absolute legend, no cap 🫢


and that's the whole page bestie, you ate πŸ’… this was perl v5.40.1, dated 2026-02-21, from the User Contributed Perl Documentation, filed under IO::As...untdown(3pm) β€” slay, go forth and count down πŸŒˆβœ¨πŸŽ€

πŸ“¦ where this page comes from