Skip to content

Commit ad00a85

Browse files
authored
Add throttle method to LazyCollection (#51060)
1 parent 8f42dc0 commit ad00a85

File tree

3 files changed

+125
-0
lines changed

3 files changed

+125
-0
lines changed

src/Illuminate/Collections/LazyCollection.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1570,6 +1570,28 @@ public function tapEach(callable $callback)
15701570
});
15711571
}
15721572

1573+
/**
1574+
* Throttle the values, releasing them at most once per the given seconds.
1575+
*
1576+
* @return static<TKey, TValue>
1577+
*/
1578+
public function throttle(float $seconds)
1579+
{
1580+
return new static(function () use ($seconds) {
1581+
$microseconds = $seconds * 1_000_000;
1582+
1583+
foreach ($this as $key => $value) {
1584+
$fetchedAt = $this->preciseNow();
1585+
1586+
yield $key => $value;
1587+
1588+
$sleep = $microseconds - ($this->preciseNow() - $fetchedAt);
1589+
1590+
$this->usleep((int) $sleep);
1591+
}
1592+
});
1593+
}
1594+
15731595
/**
15741596
* Flatten a multi-dimensional associative array with dots.
15751597
*
@@ -1781,4 +1803,32 @@ protected function now()
17811803
? Carbon::now()->timestamp
17821804
: time();
17831805
}
1806+
1807+
/**
1808+
* Get the precise current time.
1809+
*
1810+
* @return float
1811+
*/
1812+
protected function preciseNow()
1813+
{
1814+
return class_exists(Carbon::class)
1815+
? Carbon::now()->getPreciseTimestamp()
1816+
: microtime(true) * 1_000_000;
1817+
}
1818+
1819+
/**
1820+
* Sleep for the given amount of microseconds.
1821+
*
1822+
* @return void
1823+
*/
1824+
protected function usleep(int $microseconds)
1825+
{
1826+
if ($microseconds <= 0) {
1827+
return;
1828+
}
1829+
1830+
class_exists(Sleep::class)
1831+
? Sleep::usleep($microseconds)
1832+
: usleep($microseconds);
1833+
}
17841834
}

tests/Support/SupportLazyCollectionIsLazyTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Illuminate\Support\ItemNotFoundException;
88
use Illuminate\Support\LazyCollection;
99
use Illuminate\Support\MultipleItemsFoundException;
10+
use Illuminate\Support\Sleep;
1011
use Mockery as m;
1112
use PHPUnit\Framework\TestCase;
1213
use stdClass;
@@ -1370,6 +1371,25 @@ public function testTapEachIsLazy()
13701371
});
13711372
}
13721373

1374+
public function testThrottleIsLazy()
1375+
{
1376+
Sleep::fake();
1377+
1378+
$this->assertDoesNotEnumerate(function ($collection) {
1379+
$collection->throttle(10);
1380+
});
1381+
1382+
$this->assertEnumerates(5, function ($collection) {
1383+
$collection->throttle(10)->take(5)->all();
1384+
});
1385+
1386+
$this->assertEnumeratesOnce(function ($collection) {
1387+
$collection->throttle(10)->all();
1388+
});
1389+
1390+
Sleep::fake(false);
1391+
}
1392+
13731393
public function testTimesIsLazy()
13741394
{
13751395
$data = LazyCollection::times(INF);

tests/Support/SupportLazyCollectionTest.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22

33
namespace Illuminate\Tests\Support;
44

5+
use Carbon\CarbonInterval as Duration;
6+
use Illuminate\Foundation\Testing\Wormhole;
57
use Illuminate\Support\Carbon;
68
use Illuminate\Support\Collection;
79
use Illuminate\Support\LazyCollection;
10+
use Illuminate\Support\Sleep;
811
use InvalidArgumentException;
912
use Mockery as m;
1013
use PHPUnit\Framework\TestCase;
@@ -223,6 +226,58 @@ public function testTapEach()
223226
$this->assertSame([1, 2, 3, 4, 5], $tapped);
224227
}
225228

229+
public function testThrottle()
230+
{
231+
Sleep::fake();
232+
233+
$data = LazyCollection::times(3)
234+
->throttle(2)
235+
->all();
236+
237+
Sleep::assertSlept(function (Duration $duration) {
238+
$this->assertEqualsWithDelta(
239+
2_000_000, $duration->totalMicroseconds, 1_000
240+
);
241+
242+
return true;
243+
}, times: 3);
244+
245+
$this->assertSame([1, 2, 3], $data);
246+
247+
Sleep::fake(false);
248+
}
249+
250+
public function testThrottleAccountsForTimePassed()
251+
{
252+
Sleep::fake();
253+
Carbon::setTestNow(now());
254+
255+
$data = LazyCollection::times(3)
256+
->throttle(3)
257+
->tapEach(function ($value, $index) {
258+
if ($index == 1) {
259+
// Travel in time...
260+
(new Wormhole(1))->second();
261+
}
262+
})
263+
->all();
264+
265+
Sleep::assertSlept(function (Duration $duration, int $index) {
266+
$expectation = $index == 1 ? 2_000_000 : 3_000_000;
267+
268+
$this->assertEqualsWithDelta(
269+
$expectation, $duration->totalMicroseconds, 1_000
270+
);
271+
272+
return true;
273+
}, times: 3);
274+
275+
$this->assertSame([1, 2, 3], $data);
276+
277+
Sleep::fake(false);
278+
Carbon::setTestNow();
279+
}
280+
226281
public function testUniqueDoubleEnumeration()
227282
{
228283
$data = LazyCollection::times(2)->unique();

0 commit comments

Comments
 (0)