Skip to content

Commit a0d5549

Browse files
authored
Add Interval (#441)
1 parent 3f3a4f8 commit a0d5549

File tree

2 files changed

+147
-0
lines changed

2 files changed

+147
-0
lines changed

src/Interval.php

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Amp;
4+
5+
use Revolt\EventLoop;
6+
7+
/**
8+
* This object invokes the given callback within a new coroutine every $interval seconds until either the
9+
* {@see self::disable()} method is called or the object is destroyed.
10+
*/
11+
final class Interval
12+
{
13+
private readonly string $callbackId;
14+
15+
/**
16+
* @param float $interval Invoke the function every $interval seconds.
17+
* @param \Closure():void $closure Use {@see weakClosure()} to avoid a circular reference if storing this object
18+
* as a property of another object.
19+
* @param bool $reference If false, unreference the underlying watcher.
20+
*/
21+
public function __construct(float $interval, \Closure $closure, bool $reference = true)
22+
{
23+
$this->callbackId = EventLoop::repeat($interval, $closure);
24+
25+
if (!$reference) {
26+
EventLoop::unreference($this->callbackId);
27+
}
28+
}
29+
30+
public function __destruct()
31+
{
32+
EventLoop::cancel($this->callbackId);
33+
}
34+
35+
/**
36+
* @return bool True if the internal watcher is referenced.
37+
*/
38+
public function isReferenced(): bool
39+
{
40+
return EventLoop::isReferenced($this->callbackId);
41+
}
42+
43+
/**
44+
* References the internal watcher in the event loop, keeping the loop running while the repeat loop is enabled.
45+
*
46+
* @return $this
47+
*/
48+
public function reference(): self
49+
{
50+
EventLoop::reference($this->callbackId);
51+
52+
return $this;
53+
}
54+
55+
/**
56+
* Unreferences the internal watcher in the event loop, allowing the loop to stop while the repeat loop is enabled.
57+
*
58+
* @return $this
59+
*/
60+
public function unreference(): self
61+
{
62+
EventLoop::unreference($this->callbackId);
63+
64+
return $this;
65+
}
66+
67+
/**
68+
* @return bool True if the repeating timer is enabled.
69+
*/
70+
public function isEnabled(): bool
71+
{
72+
return EventLoop::isEnabled($this->callbackId);
73+
}
74+
75+
/**
76+
* Restart the repeating timer if previously stopped with {@see self::disable()}.
77+
*
78+
* @return $this
79+
*/
80+
public function enable(): self
81+
{
82+
EventLoop::enable($this->callbackId);
83+
84+
return $this;
85+
}
86+
87+
/**
88+
* Stop the repeating timer. Restart it with {@see self::enable()}.
89+
*
90+
* @return $this
91+
*/
92+
public function disable(): self
93+
{
94+
EventLoop::disable($this->callbackId);
95+
96+
return $this;
97+
}
98+
}

test/IntervalTest.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Amp;
4+
5+
class IntervalTest extends TestCase
6+
{
7+
public function testCancelWhenDestroyed(): void
8+
{
9+
$timeout = 0.01;
10+
$invocationCount = 0;
11+
$interval = new Interval($timeout, function () use (&$invocationCount): void {
12+
++$invocationCount;
13+
});
14+
15+
delay($timeout * 1.5);
16+
17+
self::assertGreaterThan(0, $invocationCount);
18+
$originalCount = $invocationCount;
19+
20+
unset($interval);
21+
22+
delay($timeout * 10);
23+
24+
self::assertSame($originalCount, $invocationCount);
25+
}
26+
27+
public function testEnableAndDisable(): void
28+
{
29+
$timeout = 0.01;
30+
$invocationCount = 0;
31+
$interval = new Interval($timeout, function () use (&$invocationCount): void {
32+
++$invocationCount;
33+
});
34+
35+
$interval->disable();
36+
self::assertFalse($interval->isEnabled());
37+
38+
delay($timeout * 2);
39+
40+
self::assertSame(0, $invocationCount);
41+
42+
$interval->enable();
43+
self::assertTrue($interval->isEnabled());
44+
45+
delay($timeout * 1.5);
46+
47+
self::assertSame(1, $invocationCount);
48+
}
49+
}

0 commit comments

Comments
 (0)