From 514ec9c8bae9f70314809618105100680652f2d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Tue, 5 Oct 2021 21:26:32 +0200 Subject: [PATCH] Simplify usage by supporting new default loop --- README.md | 63 ++++++++++++++++++++-------------- composer.json | 4 +-- examples/01-await.php | 7 ++-- examples/02-await-any.php | 7 ++-- examples/03-await-all.php | 7 ++-- src/functions.php | 60 +++++++++++++++++++++++--------- tests/FunctionAwaitAllTest.php | 7 ++++ tests/FunctionAwaitAnyTest.php | 7 ++++ tests/FunctionAwaitTest.php | 7 ++++ tests/TestCase.php | 3 +- 10 files changed, 111 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index 3d7fa70..edf87a9 100644 --- a/README.md +++ b/README.md @@ -53,20 +53,17 @@ non-blocking HTTP requests and block until the first (faster) one resolves. ```php function blockingExample() { - // use a unique event loop instance for all parallel operations - $loop = React\EventLoop\Factory::create(); - // this example uses an HTTP client // this could be pretty much everything that binds to an event loop - $browser = new React\Http\Browser($loop); - + $browser = new React\Http\Browser(); + // set up two parallel requests $request1 = $browser->get('http://www.google.com/'); $request2 = $browser->get('http://www.google.co.uk/'); - + // keep the loop running (i.e. block) until the first response arrives - $fasterResponse = Clue\React\Block\awaitAny(array($request1, $request2), $loop); - + $fasterResponse = Clue\React\Block\awaitAny(array($request1, $request2)); + return $fasterResponse->getBody(); } ``` @@ -98,19 +95,9 @@ use Clue\React\Block; Block\await(…); ``` -### EventLoop - -Each function is responsible for orchestrating the -[`EventLoop`](https://github.com/reactphp/event-loop#usage) -in order to make it run (block) until your conditions are fulfilled. - -```php -$loop = React\EventLoop\Factory::create(); -``` - ### sleep() -The `sleep($seconds, LoopInterface $loop): void` function can be used to +The `sleep(float $seconds, ?LoopInterface $loop = null): void` function can be used to wait/sleep for `$time` seconds. ```php @@ -127,13 +114,19 @@ it keeps running until this timer 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. + ### await() -The `await(PromiseInterface $promise, LoopInterface $loop, ?float $timeout = null): mixed` function can be used to +The `await(PromiseInterface $promise, ?LoopInterface $loop = null, ?float $timeout = null): mixed` function can be used to block waiting for the given `$promise` to be fulfilled. ```php -$result = Clue\React\Block\await($promise, $loop); +$result = Clue\React\Block\await($promise); ``` This function will only return after the given `$promise` has settled, i.e. @@ -149,7 +142,7 @@ will throw an `UnexpectedValueException` instead. ```php try { - $result = Clue\React\Block\await($promise, $loop); + $result = Clue\React\Block\await($promise); // promise successfully fulfilled with $result echo 'Result: ' . $result; } catch (Exception $exception) { @@ -160,6 +153,12 @@ try { See also the [examples](examples/). +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. + If no `$timeout` argument is given and the promise stays pending, then this will potentially wait/block forever until the promise is settled. To avoid this, API authors creating promises are expected to provide means to @@ -173,7 +172,7 @@ start a timer and will thus trigger at the earliest possible time in the future. ### awaitAny() -The `awaitAny(PromiseInterface[] $promises, LoopInterface $loop, ?float $timeout = null): mixed` function can be used to +The `awaitAny(PromiseInterface[] $promises, ?LoopInterface $loop = null, ?float $timeout = null): mixed` function can be used to wait for ANY of the given promises to be fulfilled. ```php @@ -182,7 +181,7 @@ $promises = array( $promise2 ); -$firstResult = Clue\React\Block\awaitAny($promises, $loop); +$firstResult = Clue\React\Block\awaitAny($promises); echo 'First result: ' . $firstResult; ``` @@ -199,6 +198,12 @@ promise resolved to and will try to `cancel()` all remaining promises. Once ALL promises reject, this function will fail and throw an `UnderflowException`. Likewise, this will throw if an empty array of `$promises` is passed. +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. + If no `$timeout` argument is given and ALL promises stay pending, then this will potentially wait/block forever until the promise is fulfilled. To avoid this, API authors creating promises are expected to provide means to @@ -213,7 +218,7 @@ possible time in the future. ### awaitAll() -The `awaitAll(PromiseInterface[] $promises, LoopInterface $loop, ?float $timeout = null): mixed[]` function can be used to +The `awaitAll(PromiseInterface[] $promises, ?LoopInterface $loop = null, ?float $timeout = null): mixed[]` function can be used to wait for ALL of the given promises to be fulfilled. ```php @@ -222,7 +227,7 @@ $promises = array( $promise2 ); -$allResults = Clue\React\Block\awaitAll($promises, $loop); +$allResults = Clue\React\Block\awaitAll($promises); echo 'First promise resolved with: ' . $allResults[0]; ``` @@ -242,6 +247,12 @@ Once ANY promise rejects, this will try to `cancel()` all remaining promises and throw an `Exception`. If the promise did not reject with an `Exception`, then this function will throw an `UnexpectedValueException` instead. +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. + If no `$timeout` argument is given and ANY promises stay pending, then this will potentially wait/block forever until the promise is fulfilled. To avoid this, API authors creating promises are expected to provide means to diff --git a/composer.json b/composer.json index 4545462..6102250 100644 --- a/composer.json +++ b/composer.json @@ -18,12 +18,12 @@ }, "require": { "php": ">=5.3", - "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", + "react/event-loop": "^1.2", "react/promise": "^2.7 || ^1.2.1", "react/promise-timer": "^1.5" }, "require-dev": { "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35", - "react/http": "^1.0" + "react/http": "^1.4" } } diff --git a/examples/01-await.php b/examples/01-await.php index 8e61f2b..e11cd62 100644 --- a/examples/01-await.php +++ b/examples/01-await.php @@ -9,18 +9,15 @@ */ function requestHttp($url) { - // use a unique event loop instance for this operation - $loop = React\EventLoop\Factory::create(); - // This example uses an HTTP client - $browser = new React\Http\Browser($loop); + $browser = new React\Http\Browser(); // set up one request $promise = $browser->get($url); try { // keep the loop running (i.e. block) until the response arrives - $result = Clue\React\Block\await($promise, $loop); + $result = Clue\React\Block\await($promise); // promise successfully fulfilled with $result return $result; diff --git a/examples/02-await-any.php b/examples/02-await-any.php index e7578a3..9c8e534 100644 --- a/examples/02-await-any.php +++ b/examples/02-await-any.php @@ -10,11 +10,8 @@ */ function requestHttpFastestOfMultiple($url1, $url2) { - // use a unique event loop instance for all parallel operations - $loop = React\EventLoop\Factory::create(); - // This example uses an HTTP client - $browser = new React\Http\Browser($loop); + $browser = new React\Http\Browser(); // set up two parallel requests $promises = array( @@ -24,7 +21,7 @@ function requestHttpFastestOfMultiple($url1, $url2) try { // keep the loop running (i.e. block) until the first response arrives - $fasterResponse = Clue\React\Block\awaitAny($promises, $loop); + $fasterResponse = Clue\React\Block\awaitAny($promises); // promise successfully fulfilled with $fasterResponse return $fasterResponse; diff --git a/examples/03-await-all.php b/examples/03-await-all.php index 62ed03c..26dc18c 100644 --- a/examples/03-await-all.php +++ b/examples/03-await-all.php @@ -10,11 +10,8 @@ */ function requestHttpMultiple($url1, $url2) { - // use a unique event loop instance for all parallel operations - $loop = React\EventLoop\Factory::create(); - // This example uses an HTTP client - $browser = new React\Http\Browser($loop); + $browser = new React\Http\Browser(); // set up two parallel requests $promises = array( @@ -24,7 +21,7 @@ function requestHttpMultiple($url1, $url2) try { // keep the loop running (i.e. block) until all responses arrive - $allResults = Clue\React\Block\awaitAll($promises, $loop); + $allResults = Clue\React\Block\awaitAll($promises); // promise successfully fulfilled with $allResults return $allResults; diff --git a/src/functions.php b/src/functions.php index 6e0d1d0..ae98981 100644 --- a/src/functions.php +++ b/src/functions.php @@ -2,14 +2,15 @@ namespace Clue\React\Block; +use React\EventLoop\Loop; use React\EventLoop\LoopInterface; -use React\Promise\PromiseInterface; -use React\Promise\CancellablePromiseInterface; -use UnderflowException; -use Exception; use React\Promise; +use React\Promise\CancellablePromiseInterface; +use React\Promise\PromiseInterface; use React\Promise\Timer; use React\Promise\Timer\TimeoutException; +use Exception; +use UnderflowException; /** * Wait/sleep for `$time` seconds. @@ -28,11 +29,17 @@ * 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. + * * @param float $time - * @param LoopInterface $loop + * @param ?LoopInterface $loop * @return void */ -function sleep($time, LoopInterface $loop) +function sleep($time, LoopInterface $loop = null) { await(Timer\resolve($time, $loop), $loop); } @@ -68,6 +75,12 @@ function sleep($time, LoopInterface $loop) * * See also the [examples](../examples/). * + * 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. + * * If no `$timeout` argument is given and the promise stays pending, then this * will potentially wait/block forever until the promise is settled. To avoid * this, API authors creating promises are expected to provide means to @@ -80,18 +93,19 @@ function sleep($time, LoopInterface $loop) * start a timer and will thus trigger at the earliest possible time in the future. * * @param PromiseInterface $promise - * @param LoopInterface $loop - * @param null|float $timeout [deprecated] (optional) maximum timeout in seconds or null=wait forever + * @param ?LoopInterface $loop + * @param ?float $timeout [deprecated] (optional) maximum timeout in seconds or null=wait forever * @return mixed returns whatever the promise resolves to * @throws Exception when the promise is rejected * @throws TimeoutException if the $timeout is given and triggers */ -function await(PromiseInterface $promise, LoopInterface $loop, $timeout = null) +function await(PromiseInterface $promise, LoopInterface $loop = null, $timeout = null) { $wait = true; $resolved = null; $exception = null; $rejected = false; + $loop = $loop ?: Loop::get(); if ($timeout !== null) { $promise = Timer\timeout($promise, $timeout, $loop); @@ -164,6 +178,12 @@ function ($error) use (&$exception, &$rejected, &$wait, $loop) { * Once ALL promises reject, this function will fail and throw an `UnderflowException`. * Likewise, this will throw if an empty array of `$promises` is passed. * + * 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. + * * If no `$timeout` argument is given and ALL promises stay pending, then this * will potentially wait/block forever until the promise is fulfilled. To avoid * this, API authors creating promises are expected to provide means to @@ -176,14 +196,14 @@ function ($error) use (&$exception, &$rejected, &$wait, $loop) { * value, it will still start a timer and will thus trigger at the earliest * possible time in the future. * - * @param array $promises - * @param LoopInterface $loop - * @param null|float $timeout [deprecated] (optional) maximum timeout in seconds or null=wait forever + * @param array $promises + * @param ?LoopInterface $loop + * @param ?float $timeout [deprecated] (optional) maximum timeout in seconds or null=wait forever * @return mixed returns whatever the first promise resolves to * @throws Exception if ALL promises are rejected * @throws TimeoutException if the $timeout is given and triggers */ -function awaitAny(array $promises, LoopInterface $loop, $timeout = null) +function awaitAny(array $promises, LoopInterface $loop = null, $timeout = null) { // Explicitly overwrite argument with null value. This ensure that this // argument does not show up in the stack trace in PHP 7+ only. @@ -249,6 +269,12 @@ function awaitAny(array $promises, LoopInterface $loop, $timeout = null) * and throw an `Exception`. If the promise did not reject with an `Exception`, * then this function will throw an `UnexpectedValueException` instead. * + * 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. + * * If no `$timeout` argument is given and ANY promises stay pending, then this * will potentially wait/block forever until the promise is fulfilled. To avoid * this, API authors creating promises are expected to provide means to @@ -261,14 +287,14 @@ function awaitAny(array $promises, LoopInterface $loop, $timeout = null) * value, it will still start a timer and will thus trigger at the earliest * possible time in the future. * - * @param array $promises - * @param LoopInterface $loop - * @param null|float $timeout [deprecated] (optional) maximum timeout in seconds or null=wait forever + * @param array $promises + * @param ?LoopInterface $loop + * @param ?float $timeout [deprecated] (optional) maximum timeout in seconds or null=wait forever * @return array returns an array with whatever each promise resolves to * @throws Exception when ANY promise is rejected * @throws TimeoutException if the $timeout is given and triggers */ -function awaitAll(array $promises, LoopInterface $loop, $timeout = null) +function awaitAll(array $promises, LoopInterface $loop = null, $timeout = null) { // Explicitly overwrite argument with null value. This ensure that this // argument does not show up in the stack trace in PHP 7+ only. diff --git a/tests/FunctionAwaitAllTest.php b/tests/FunctionAwaitAllTest.php index 9532657..cc6e645 100644 --- a/tests/FunctionAwaitAllTest.php +++ b/tests/FunctionAwaitAllTest.php @@ -23,6 +23,13 @@ public function testAwaitAllAllResolved() $this->assertEquals(array('first' => 1, 'second' => 2), Block\awaitAll($all, $this->loop)); } + public function testAwaitAllReturnsArrayWithFulfilledValueFromSinglePromiseWithoutGivingLoop() + { + $promise = Promise\resolve(42); + + $this->assertEquals(array(42), Block\awaitAll(array($promise))); + } + public function testAwaitAllRejected() { $all = array( diff --git a/tests/FunctionAwaitAnyTest.php b/tests/FunctionAwaitAnyTest.php index faded88..58539dd 100644 --- a/tests/FunctionAwaitAnyTest.php +++ b/tests/FunctionAwaitAnyTest.php @@ -26,6 +26,13 @@ public function testAwaitAnyFirstResolved() $this->assertEquals(2, Block\awaitAny($all, $this->loop)); } + public function testAwaitAnyReturnsFulfilledValueFromSinglePromiseWithoutGivingLoop() + { + $promise = Promise\resolve(42); + + $this->assertEquals(42, Block\awaitAny(array($promise))); + } + public function testAwaitAnyFirstResolvedConcurrently() { $d1 = new Deferred(); diff --git a/tests/FunctionAwaitTest.php b/tests/FunctionAwaitTest.php index 68fd2d2..41c95f5 100644 --- a/tests/FunctionAwaitTest.php +++ b/tests/FunctionAwaitTest.php @@ -58,6 +58,13 @@ public function testAwaitOneResolved() $this->assertEquals(2, Block\await($promise, $this->loop)); } + public function testAwaitReturnsFulfilledValueWithoutGivingLoop() + { + $promise = Promise\resolve(42); + + $this->assertEquals(42, Block\await($promise)); + } + public function testAwaitOneInterrupted() { $promise = $this->createPromiseResolved(2, 0.02); diff --git a/tests/TestCase.php b/tests/TestCase.php index 6ef25bc..40730bd 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -3,6 +3,7 @@ namespace Clue\Tests\React\Block; use PHPUnit\Framework\TestCase as BaseTestCase; +use React\EventLoop\Loop; use React\Promise\Deferred; class TestCase extends BaseTestCase @@ -14,7 +15,7 @@ class TestCase extends BaseTestCase */ public function setUpLoop() { - $this->loop = \React\EventLoop\Factory::create(); + $this->loop = Loop::get(); } protected function createPromiseResolved($value = null, $delay = 0.01)