Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify usage by supporting new default loop #60

Merged
merged 1 commit into from
Oct 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 37 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
```
Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand All @@ -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) {
Expand All @@ -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
Expand All @@ -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
Expand All @@ -182,7 +181,7 @@ $promises = array(
$promise2
);

$firstResult = Clue\React\Block\awaitAny($promises, $loop);
$firstResult = Clue\React\Block\awaitAny($promises);

echo 'First result: ' . $firstResult;
```
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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];
```
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
7 changes: 2 additions & 5 deletions examples/01-await.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
7 changes: 2 additions & 5 deletions examples/02-await-any.php
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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;
Expand Down
7 changes: 2 additions & 5 deletions examples/03-await-all.php
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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;
Expand Down
60 changes: 43 additions & 17 deletions src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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);
}
Expand Down Expand Up @@ -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
Expand All @@ -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);
Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand Down
7 changes: 7 additions & 0 deletions tests/FunctionAwaitAllTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Loading