Skip to content

Simplify usage by supporting new default loop #46

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

Merged
merged 2 commits into from
Jul 11, 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
46 changes: 32 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
});
```
Expand All @@ -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
},
Expand All @@ -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
})
Expand Down Expand Up @@ -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();
```
Expand All @@ -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();
```
Expand Down Expand Up @@ -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();
```
Expand All @@ -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
});
```
Expand All @@ -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;
});
```
Expand All @@ -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();
```
Expand All @@ -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;
});
```
Expand All @@ -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.

Expand All @@ -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();
```
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
17 changes: 13 additions & 4 deletions src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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) {
Expand Down Expand Up @@ -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) {
Expand All @@ -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');
Expand Down
20 changes: 10 additions & 10 deletions tests/FunctionRejectTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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());
Expand All @@ -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);

Expand Down
21 changes: 11 additions & 10 deletions tests/FunctionResolveTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -62,7 +63,7 @@ public function testCancellingPromiseWillCancelLoopTimer()

public function testCancellingPromiseWillRejectTimer()
{
$promise = Timer\resolve(0.01, $this->loop);
$promise = Timer\resolve(0.01);

$promise->cancel();

Expand All @@ -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());
Expand All @@ -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);

Expand Down
Loading