diff --git a/README.md b/README.md index b1c5220..2e950ec 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Alternatively, you can also refer to them with their fully-qualified name: ### timeout() -The `timeout(PromiseInterface $promise, $time, LoopInterface $loop)` function +The `timeout(PromiseInterface $promise, $time, LoopInterface $loop = null)` function can be used to *cancel* operations that take *too long*. You need to pass in an input `$promise` that represents a pending operation and timeout parameters. It returns a new `Promise` with the following resolution behavior: @@ -60,11 +60,17 @@ start a timer and will thus trigger at the earliest possible time in the future. If the input `$promise` is already settled, then the resulting promise will resolve or reject immediately without starting a timer at all. +This function takes an optional `LoopInterface|null $loop` parameter that can be used to +pass the event loop instance to use. You can use a `null` value here in order to +use the [default loop](https://github.com/reactphp/event-loop#loop). This value +SHOULD NOT be given unless you're sure you want to explicitly use a given event +loop instance. + A common use case for handling only resolved values looks like this: ```php $promise = accessSomeRemoteResource(); -Timer\timeout($promise, 10.0, $loop)->then(function ($value) { +Timer\timeout($promise, 10.0)->then(function ($value) { // the operation finished within 10.0 seconds }); ``` @@ -73,7 +79,7 @@ A more complete example could look like this: ```php $promise = accessSomeRemoteResource(); -Timer\timeout($promise, 10.0, $loop)->then( +Timer\timeout($promise, 10.0)->then( function ($value) { // the operation finished within 10.0 seconds }, @@ -90,7 +96,7 @@ Timer\timeout($promise, 10.0, $loop)->then( Or if you're using [react/promise v2.2.0](https://github.com/reactphp/promise) or up: ```php -Timer\timeout($promise, 10.0, $loop) +Timer\timeout($promise, 10.0) ->then(function ($value) { // the operation finished within 10.0 seconds }) @@ -172,7 +178,7 @@ input `$promise`, as demonstrated in the following example: ```php $promise = accessSomeRemoteResource(); -$timeout = Timer\timeout($promise, 10.0, $loop); +$timeout = Timer\timeout($promise, 10.0); $promise->cancel(); ``` @@ -195,7 +201,7 @@ Similarily, you can also explicitly `cancel()` the resulting promise like this: ```php $promise = accessSomeRemoteResource(); -$timeout = Timer\timeout($promise, 10.0, $loop); +$timeout = Timer\timeout($promise, 10.0); $timeout->cancel(); ``` @@ -231,7 +237,7 @@ This is done for consistency with the [timeout cancellation](#timeout-cancellati handling and also because it is assumed this is often used like this: ```php -$timeout = Timer\timeout(accessSomeRemoteResource(), 10.0, $loop); +$timeout = Timer\timeout(accessSomeRemoteResource(), 10.0); $timeout->cancel(); ``` @@ -258,7 +264,7 @@ $promises = array( $promise = \React\Promise\all($promises); -Timer\timeout($promise, 10, $loop)->then(function ($values) { +Timer\timeout($promise, 10)->then(function ($values) { // *all* promises resolved }); ``` @@ -270,11 +276,11 @@ For more details on the promise primitives, please refer to the ### resolve() -The `resolve($time, LoopInterface $loop)` function can be used to create a new Promise that +The `resolve($time, LoopInterface $loop = null)` function can be used to create a new Promise that resolves in `$time` seconds with the `$time` as the fulfillment value. ```php -Timer\resolve(1.5, $loop)->then(function ($time) { +Timer\resolve(1.5)->then(function ($time) { echo 'Thanks for waiting ' . $time . ' seconds' . PHP_EOL; }); ``` @@ -284,12 +290,18 @@ resolve the promise once it triggers. This implies that if you pass a really small (or negative) value, it will still start a timer and will thus trigger at the earliest possible time in the future. +This function takes an optional `LoopInterface|null $loop` parameter that can be used to +pass the event loop instance to use. You can use a `null` value here in order to +use the [default loop](https://github.com/reactphp/event-loop#loop). This value +SHOULD NOT be given unless you're sure you want to explicitly use a given event +loop instance. + #### Resolve cancellation You can explicitly `cancel()` the resulting timer promise at any time: ```php -$timer = Timer\resolve(2.0, $loop); +$timer = Timer\resolve(2.0); $timer->cancel(); ``` @@ -298,11 +310,11 @@ This will abort the timer and *reject* with a `RuntimeException`. ### reject() -The `reject($time, LoopInterface $loop)` function can be used to create a new Promise +The `reject($time, LoopInterface $loop = null)` function can be used to create a new Promise which rejects in `$time` seconds with a `TimeoutException`. ```php -Timer\reject(2.0, $loop)->then(null, function (TimeoutException $e) { +Timer\reject(2.0)->then(null, function (TimeoutException $e) { echo 'Rejected after ' . $e->getTimeout() . ' seconds ' . PHP_EOL; }); ``` @@ -312,6 +324,12 @@ reject the promise once it triggers. This implies that if you pass a really small (or negative) value, it will still start a timer and will thus trigger at the earliest possible time in the future. +This function takes an optional `LoopInterface|null $loop` parameter that can be used to +pass the event loop instance to use. You can use a `null` value here in order to +use the [default loop](https://github.com/reactphp/event-loop#loop). This value +SHOULD NOT be given unless you're sure you want to explicitly use a given event +loop instance. + This function complements the [`resolve()`](#resolve) function and can be used as a basic building block for higher-level promise consumers. @@ -320,7 +338,7 @@ and can be used as a basic building block for higher-level promise consumers. You can explicitly `cancel()` the resulting timer promise at any time: ```php -$timer = Timer\reject(2.0, $loop); +$timer = Timer\reject(2.0); $timer->cancel(); ``` diff --git a/composer.json b/composer.json index 13679d6..7335298 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,7 @@ }, "require": { "php": ">=5.3", - "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", + "react/event-loop": "^1.2", "react/promise": "^3.0 || ^2.7.0 || ^1.2.1" }, "require-dev": { diff --git a/src/functions.php b/src/functions.php index 32de9fc..c82a144 100644 --- a/src/functions.php +++ b/src/functions.php @@ -2,12 +2,13 @@ namespace React\Promise\Timer; +use React\EventLoop\Loop; use React\EventLoop\LoopInterface; use React\Promise\CancellablePromiseInterface; -use React\Promise\PromiseInterface; use React\Promise\Promise; +use React\Promise\PromiseInterface; -function timeout(PromiseInterface $promise, $time, LoopInterface $loop) +function timeout(PromiseInterface $promise, $time, LoopInterface $loop = null) { // cancelling this promise will only try to cancel the input promise, // thus leaving responsibility to the input promise. @@ -21,6 +22,10 @@ function timeout(PromiseInterface $promise, $time, LoopInterface $loop) }; } + if ($loop === null) { + $loop = Loop::get(); + } + return new Promise(function ($resolve, $reject) use ($loop, $time, $promise) { $timer = null; $promise = $promise->then(function ($v) use (&$timer, $loop, $resolve) { @@ -56,8 +61,12 @@ function timeout(PromiseInterface $promise, $time, LoopInterface $loop) }, $canceller); } -function resolve($time, LoopInterface $loop) +function resolve($time, LoopInterface $loop = null) { + if ($loop === null) { + $loop = Loop::get(); + } + return new Promise(function ($resolve) use ($loop, $time, &$timer) { // resolve the promise when the timer fires in $time seconds $timer = $loop->addTimer($time, function () use ($time, $resolve) { @@ -73,7 +82,7 @@ function resolve($time, LoopInterface $loop) }); } -function reject($time, LoopInterface $loop) +function reject($time, LoopInterface $loop = null) { return resolve($time, $loop)->then(function ($time) { throw new TimeoutException($time, 'Timer expired after ' . $time . ' seconds'); diff --git a/tests/FunctionRejectTest.php b/tests/FunctionRejectTest.php index be944e1..f5ddcbf 100644 --- a/tests/FunctionRejectTest.php +++ b/tests/FunctionRejectTest.php @@ -2,45 +2,46 @@ namespace React\Tests\Promise\Timer; +use React\EventLoop\Loop; use React\Promise\Timer; class FunctionRejectTest extends TestCase { public function testPromiseIsPendingWithoutRunningLoop() { - $promise = Timer\reject(0.01, $this->loop); + $promise = Timer\reject(0.01); $this->expectPromisePending($promise); } public function testPromiseExpiredIsPendingWithoutRunningLoop() { - $promise = Timer\reject(-1, $this->loop); + $promise = Timer\reject(-1); $this->expectPromisePending($promise); } public function testPromiseWillBeRejectedOnTimeout() { - $promise = Timer\reject(0.01, $this->loop); + $promise = Timer\reject(0.01); - $this->loop->run(); + Loop::run(); $this->expectPromiseRejected($promise); } public function testPromiseExpiredWillBeRejectedOnTimeout() { - $promise = Timer\reject(-1, $this->loop); + $promise = Timer\reject(-1); - $this->loop->run(); + Loop::run(); $this->expectPromiseRejected($promise); } public function testCancellingPromiseWillRejectTimer() { - $promise = Timer\reject(0.01, $this->loop); + $promise = Timer\reject(0.01); $promise->cancel(); @@ -56,8 +57,7 @@ public function testWaitingForPromiseToRejectDoesNotLeaveGarbageCycles() gc_collect_cycles(); gc_collect_cycles(); // clear twice to avoid leftovers in PHP 7.4 with ext-xdebug and code coverage turned on - $promise = Timer\reject(0.01, $this->loop); - $this->loop->run(); + $promise = Timer\reject(0.01); unset($promise); $this->assertEquals(0, gc_collect_cycles()); @@ -71,7 +71,7 @@ public function testCancellingPromiseDoesNotLeaveGarbageCycles() gc_collect_cycles(); - $promise = Timer\reject(0.01, $this->loop); + $promise = Timer\reject(0.01); $promise->cancel(); unset($promise); diff --git a/tests/FunctionResolveTest.php b/tests/FunctionResolveTest.php index c4e2be7..b6e0187 100644 --- a/tests/FunctionResolveTest.php +++ b/tests/FunctionResolveTest.php @@ -2,38 +2,39 @@ namespace React\Tests\Promise\Timer; +use React\EventLoop\Loop; use React\Promise\Timer; class FunctionResolveTest extends TestCase { public function testPromiseIsPendingWithoutRunningLoop() { - $promise = Timer\resolve(0.01, $this->loop); + $promise = Timer\resolve(0.01); $this->expectPromisePending($promise); } public function testPromiseExpiredIsPendingWithoutRunningLoop() { - $promise = Timer\resolve(-1, $this->loop); + $promise = Timer\resolve(-1); $this->expectPromisePending($promise); } public function testPromiseWillBeResolvedOnTimeout() { - $promise = Timer\resolve(0.01, $this->loop); + $promise = Timer\resolve(0.01); - $this->loop->run(); + Loop::run(); $this->expectPromiseResolved($promise); } public function testPromiseExpiredWillBeResolvedOnTimeout() { - $promise = Timer\resolve(-1, $this->loop); + $promise = Timer\resolve(-1); - $this->loop->run(); + Loop::run(); $this->expectPromiseResolved($promise); } @@ -62,7 +63,7 @@ public function testCancellingPromiseWillCancelLoopTimer() public function testCancellingPromiseWillRejectTimer() { - $promise = Timer\resolve(0.01, $this->loop); + $promise = Timer\resolve(0.01); $promise->cancel(); @@ -77,8 +78,8 @@ public function testWaitingForPromiseToResolveDoesNotLeaveGarbageCycles() gc_collect_cycles(); - $promise = Timer\resolve(0.01, $this->loop); - $this->loop->run(); + $promise = Timer\resolve(0.01); + Loop::run(); unset($promise); $this->assertEquals(0, gc_collect_cycles()); @@ -92,7 +93,7 @@ public function testCancellingPromiseDoesNotLeaveGarbageCycles() gc_collect_cycles(); - $promise = Timer\resolve(0.01, $this->loop); + $promise = Timer\resolve(0.01); $promise->cancel(); unset($promise); diff --git a/tests/FunctionTimeoutTest.php b/tests/FunctionTimeoutTest.php index 47a7662..b66cf6e 100644 --- a/tests/FunctionTimeoutTest.php +++ b/tests/FunctionTimeoutTest.php @@ -2,8 +2,9 @@ namespace React\Tests\Promise\Timer; -use React\Promise\Timer; +use React\EventLoop\Loop; use React\Promise; +use React\Promise\Timer; class FunctionTimeoutTest extends TestCase { @@ -11,7 +12,7 @@ public function testResolvedWillResolveRightAway() { $promise = Promise\resolve(); - $promise = Timer\timeout($promise, 3, $this->loop); + $promise = Timer\timeout($promise, 3); $this->expectPromiseResolved($promise); } @@ -20,7 +21,7 @@ public function testResolvedExpiredWillResolveRightAway() { $promise = Promise\resolve(); - $promise = Timer\timeout($promise, -1, $this->loop); + $promise = Timer\timeout($promise, -1); $this->expectPromiseResolved($promise); } @@ -29,10 +30,10 @@ public function testResolvedWillNotStartTimer() { $promise = Promise\resolve(); - Timer\timeout($promise, 3, $this->loop); + Timer\timeout($promise, 3); $time = microtime(true); - $this->loop->run(); + Loop::run(); $time = microtime(true) - $time; $this->assertLessThan(0.5, $time); @@ -42,7 +43,7 @@ public function testRejectedWillRejectRightAway() { $promise = Promise\reject(new \Exception('reject')); - $promise = Timer\timeout($promise, 3, $this->loop); + $promise = Timer\timeout($promise, 3); $this->expectPromiseRejected($promise); } @@ -51,10 +52,10 @@ public function testRejectedWillNotStartTimer() { $promise = Promise\reject(new \Exception('reject')); - Timer\timeout($promise, 3, $this->loop); + Timer\timeout($promise, 3); $time = microtime(true); - $this->loop->run(); + Loop::run(); $time = microtime(true) - $time; $this->assertLessThan(0.5, $time); @@ -64,9 +65,9 @@ public function testPendingWillRejectOnTimeout() { $promise = $this->getMockBuilder('React\Promise\PromiseInterface')->getMock(); - $promise = Timer\timeout($promise, 0.01, $this->loop); + $promise = Timer\timeout($promise, 0.01); - $this->loop->run(); + Loop::run(); $this->expectPromiseRejected($promise); } @@ -81,9 +82,9 @@ public function testPendingCancellableWillBeCancelledThroughFollowerOnTimeout() $promise = $this->getMockBuilder($cancellableInterface)->getMock(); $promise->expects($this->once())->method('then')->willReturn($cancellable); - Timer\timeout($promise, 0.01, $this->loop); + Timer\timeout($promise, 0.01); - $this->loop->run(); + Loop::run(); } public function testCancelTimeoutWithoutCancellationhandlerWillNotCancelTimerAndWillNotReject() @@ -131,7 +132,7 @@ public function testCancelTimeoutWillCancelGivenPromise() { $promise = new \React\Promise\Promise(function () { }, $this->expectCallableOnce()); - $timeout = Timer\timeout($promise, 0.01, $this->loop); + $timeout = Timer\timeout($promise, 0.01); $timeout->cancel(); } @@ -140,7 +141,7 @@ public function testCancelGivenPromiseWillReject() { $promise = new \React\Promise\Promise(function () { }, function ($resolve, $reject) { $reject(); }); - $timeout = Timer\timeout($promise, 0.01, $this->loop); + $timeout = Timer\timeout($promise, 0.01); $promise->cancel(); @@ -152,7 +153,7 @@ public function testCancelTimeoutWillRejectIfGivenPromiseWillReject() { $promise = new \React\Promise\Promise(function () { }, function ($resolve, $reject) { $reject(); }); - $timeout = Timer\timeout($promise, 0.01, $this->loop); + $timeout = Timer\timeout($promise, 0.01); $timeout->cancel(); @@ -164,7 +165,7 @@ public function testCancelTimeoutWillResolveIfGivenPromiseWillResolve() { $promise = new \React\Promise\Promise(function () { }, function ($resolve, $reject) { $resolve(); }); - $timeout = Timer\timeout($promise, 0.01, $this->loop); + $timeout = Timer\timeout($promise, 0.01); $timeout->cancel(); @@ -180,11 +181,11 @@ public function testWaitingForPromiseToResolveBeforeTimeoutDoesNotLeaveGarbageCy gc_collect_cycles(); - $promise = Timer\resolve(0.01, $this->loop); + $promise = Timer\resolve(0.01); - $promise = Timer\timeout($promise, 1.0, $this->loop); + $promise = Timer\timeout($promise, 1.0); - $this->loop->run(); + Loop::run(); unset($promise); $this->assertEquals(0, gc_collect_cycles()); @@ -198,11 +199,11 @@ public function testWaitingForPromiseToRejectBeforeTimeoutDoesNotLeaveGarbageCyc gc_collect_cycles(); - $promise = Timer\reject(0.01, $this->loop); + $promise = Timer\reject(0.01); - $promise = Timer\timeout($promise, 1.0, $this->loop); + $promise = Timer\timeout($promise, 1.0); - $this->loop->run(); + Loop::run(); unset($promise); $this->assertEquals(0, gc_collect_cycles()); @@ -220,9 +221,9 @@ public function testWaitingForPromiseToTimeoutDoesNotLeaveGarbageCycles() throw new \RuntimeException(); }); - $promise = Timer\timeout($promise, 0.01, $this->loop); + $promise = Timer\timeout($promise, 0.01); - $this->loop->run(); + Loop::run(); unset($promise); $this->assertEquals(0, gc_collect_cycles()); @@ -238,9 +239,9 @@ public function testWaitingForPromiseToTimeoutWithoutCancellerDoesNotLeaveGarbag $promise = new \React\Promise\Promise(function () { }); - $promise = Timer\timeout($promise, 0.01, $this->loop); + $promise = Timer\timeout($promise, 0.01); - $this->loop->run(); + Loop::run(); unset($promise); $this->assertEquals(0, gc_collect_cycles()); @@ -258,9 +259,9 @@ public function testWaitingForPromiseToTimeoutWithNoOpCancellerDoesNotLeaveGarba // no-op }); - $promise = Timer\timeout($promise, 0.01, $this->loop); + $promise = Timer\timeout($promise, 0.01); - $this->loop->run(); + Loop::run(); unset($promise); $this->assertEquals(0, gc_collect_cycles()); diff --git a/tests/TestCase.php b/tests/TestCase.php index bb7af5a..6866124 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -3,20 +3,9 @@ namespace React\Tests\Promise\Timer; use PHPUnit\Framework\TestCase as BaseTestCase; -use React\EventLoop\Factory; class TestCase extends BaseTestCase { - protected $loop; - - /** - * @before - */ - public function setUpLoop() - { - $this->loop = Factory::create(); - } - protected function expectCallableOnce() { $mock = $this->createCallableMock();