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

Add catch() and finally(), deprecate otherwise() and always() #208

Merged
merged 1 commit into from
Jan 23, 2022
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
62 changes: 45 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@ Table of Contents
* [PromiseInterface](#promiseinterface)
* [PromiseInterface::then()](#promiseinterfacethen)
* [PromiseInterface::done()](#promiseinterfacedone)
* [PromiseInterface::otherwise()](#promiseinterfaceotherwise)
* [PromiseInterface::always()](#promiseinterfacealways)
* [PromiseInterface::catch()](#promiseinterfacecatch)
* [PromiseInterface::finally()](#promiseinterfacefinally)
* [PromiseInterface::cancel()](#promiseinterfacecancel)
* [~~PromiseInterface::otherwise()~~](#promiseinterfaceotherwise)
* [~~PromiseInterface::always()~~](#promiseinterfacealways)
* [Promise](#promise-2)
* [Functions](#functions)
* [resolve()](#resolve)
Expand Down Expand Up @@ -206,10 +208,10 @@ Since the purpose of `done()` is consumption rather than transformation,
* [PromiseInterface::then()](#promiseinterfacethen)
* [done() vs. then()](#done-vs-then)

#### PromiseInterface::otherwise()
#### PromiseInterface::catch()

```php
$promise->otherwise(callable $onRejected);
$promise->catch(callable $onRejected);
```

Registers a rejection handler for promise. It is a shortcut for:
Expand All @@ -223,19 +225,19 @@ only specific errors.

```php
$promise
->otherwise(function (\RuntimeException $reason) {
->catch(function (\RuntimeException $reason) {
// Only catch \RuntimeException instances
// All other types of errors will propagate automatically
})
->otherwise(function (\Throwable $reason) {
->catch(function (\Throwable $reason) {
// Catch other errors
});
```

#### PromiseInterface::always()
#### PromiseInterface::finally()

```php
$newPromise = $promise->always(callable $onFulfilledOrRejected);
$newPromise = $promise->finally(callable $onFulfilledOrRejected);
```

Allows you to execute "cleanup" type tasks in a promise chain.
Expand All @@ -254,15 +256,15 @@ when the promise is either fulfilled or rejected.
rejected promise, `$newPromise` will reject with the thrown exception or
rejected promise's reason.

`always()` behaves similarly to the synchronous finally statement. When combined
with `otherwise()`, `always()` allows you to write code that is similar to the familiar
`finally()` behaves similarly to the synchronous finally statement. When combined
with `catch()`, `finally()` allows you to write code that is similar to the familiar
synchronous catch/finally pair.

Consider the following synchronous code:

```php
try {
return doSomething();
return doSomething();
} catch (\Throwable $e) {
return handleError($e);
} finally {
Expand All @@ -275,8 +277,8 @@ written:

```php
return doSomething()
->otherwise('handleError')
->always('cleanup');
->catch('handleError')
->finally('cleanup');
```

#### PromiseInterface::cancel()
Expand All @@ -291,6 +293,32 @@ further interest in the results of the operation.
Once a promise is settled (either fulfilled or rejected), calling `cancel()` on
a promise has no effect.

#### ~~PromiseInterface::otherwise()~~

> Deprecated since v3.0.0, see [`catch()`](#promiseinterfacecatch) instead.

The `otherwise()` method registers a rejection handler for a promise.

This method continues to exist only for BC reasons and to ease upgrading
between versions. It is an alias for:

```php
$promise->catch($onRejected);
```

#### ~~PromiseInterface::always()~~

> Deprecated since v3.0.0, see [`finally()`](#promiseinterfacefinally) instead.

The `always()` method allows you to execute "cleanup" type tasks in a promise chain.

This method continues to exist only for BC reasons and to ease upgrading
between versions. It is an alias for:

```php
$promise->finally($onFulfilledOrRejected);
```

### Promise

Creates a promise whose state is controlled by the functions passed to
Expand Down Expand Up @@ -559,17 +587,17 @@ $deferred->promise()
->then(function ($x) {
throw new \Exception($x + 1);
})
->otherwise(function (\Exception $x) {
->catch(function (\Exception $x) {
// Propagate the rejection
throw $x;
})
->otherwise(function (\Exception $x) {
->catch(function (\Exception $x) {
// Can also propagate by returning another rejection
return React\Promise\reject(
new \Exception($x->getMessage() + 1)
);
})
->otherwise(function ($x) {
->catch(function ($x) {
echo 'Reject ' . $x->getMessage(); // 3
});

Expand All @@ -591,7 +619,7 @@ $deferred->promise()
->then(function ($x) {
throw new \Exception($x + 1);
})
->otherwise(function (\Exception $x) {
->catch(function (\Exception $x) {
// Handle the rejection, and don't propagate.
// This is like catch without a rethrow
return $x->getMessage() + 1;
Expand Down
22 changes: 20 additions & 2 deletions src/Internal/FulfilledPromise.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,12 @@ public function done(callable $onFulfilled = null, callable $onRejected = null):
});
}

public function otherwise(callable $onRejected): PromiseInterface
public function catch(callable $onRejected): PromiseInterface
{
return $this;
}

public function always(callable $onFulfilledOrRejected): PromiseInterface
public function finally(callable $onFulfilledOrRejected): PromiseInterface
{
return $this->then(function ($value) use ($onFulfilledOrRejected): PromiseInterface {
return resolve($onFulfilledOrRejected())->then(function () use ($value) {
Expand All @@ -77,4 +77,22 @@ public function always(callable $onFulfilledOrRejected): PromiseInterface
public function cancel(): void
{
}

/**
* @deprecated 3.0.0 Use `catch()` instead
* @see self::catch()
*/
public function otherwise(callable $onRejected): PromiseInterface
{
return $this->catch($onRejected);
}

/**
* @deprecated 3.0.0 Use `finally()` instead
* @see self::finally()
*/
public function always(callable $onFulfilledOrRejected): PromiseInterface
{
return $this->finally($onFulfilledOrRejected);
}
}
22 changes: 20 additions & 2 deletions src/Internal/RejectedPromise.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public function done(callable $onFulfilled = null, callable $onRejected = null):
});
}

public function otherwise(callable $onRejected): PromiseInterface
public function catch(callable $onRejected): PromiseInterface
{
if (!_checkTypehint($onRejected, $this->reason)) {
return $this;
Expand All @@ -70,7 +70,7 @@ public function otherwise(callable $onRejected): PromiseInterface
return $this->then(null, $onRejected);
}

public function always(callable $onFulfilledOrRejected): PromiseInterface
public function finally(callable $onFulfilledOrRejected): PromiseInterface
{
return $this->then(null, function (\Throwable $reason) use ($onFulfilledOrRejected): PromiseInterface {
return resolve($onFulfilledOrRejected())->then(function () use ($reason): PromiseInterface {
Expand All @@ -82,4 +82,22 @@ public function always(callable $onFulfilledOrRejected): PromiseInterface
public function cancel(): void
{
}

/**
* @deprecated 3.0.0 Use `catch()` instead
* @see self::catch()
*/
public function otherwise(callable $onRejected): PromiseInterface
{
return $this->catch($onRejected);
}

/**
* @deprecated 3.0.0 Use `always()` instead
* @see self::always()
*/
public function always(callable $onFulfilledOrRejected): PromiseInterface
{
return $this->finally($onFulfilledOrRejected);
}
}
22 changes: 20 additions & 2 deletions src/Promise.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public function done(callable $onFulfilled = null, callable $onRejected = null):
};
}

public function otherwise(callable $onRejected): PromiseInterface
public function catch(callable $onRejected): PromiseInterface
{
return $this->then(null, static function ($reason) use ($onRejected) {
if (!_checkTypehint($onRejected, $reason)) {
Expand All @@ -81,7 +81,7 @@ public function otherwise(callable $onRejected): PromiseInterface
});
}

public function always(callable $onFulfilledOrRejected): PromiseInterface
public function finally(callable $onFulfilledOrRejected): PromiseInterface
{
return $this->then(static function ($value) use ($onFulfilledOrRejected) {
return resolve($onFulfilledOrRejected())->then(function () use ($value) {
Expand Down Expand Up @@ -129,6 +129,24 @@ public function cancel(): void
}
}

/**
* @deprecated 3.0.0 Use `catch()` instead
* @see self::catch()
*/
public function otherwise(callable $onRejected): PromiseInterface
{
return $this->catch($onRejected);
}

/**
* @deprecated 3.0.0 Use `finally()` instead
* @see self::finally()
*/
public function always(callable $onFulfilledOrRejected): PromiseInterface
{
return $this->finally($onFulfilledOrRejected);
}

private function resolver(callable $onFulfilled = null, callable $onRejected = null): callable
{
return function ($resolve, $reject) use ($onFulfilled, $onRejected) {
Expand Down
46 changes: 40 additions & 6 deletions src/PromiseInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public function done(callable $onFulfilled = null, callable $onRejected = null):
* @param callable $onRejected
* @return PromiseInterface
*/
public function otherwise(callable $onRejected): PromiseInterface;
public function catch(callable $onRejected): PromiseInterface;

/**
* Allows you to execute "cleanup" type tasks in a promise chain.
Expand All @@ -82,8 +82,8 @@ public function otherwise(callable $onRejected): PromiseInterface;
* rejected promise, `$newPromise` will reject with the thrown exception or
* rejected promise's reason.
*
* `always()` behaves similarly to the synchronous finally statement. When combined
* with `otherwise()`, `always()` allows you to write code that is similar to the familiar
* `finally()` behaves similarly to the synchronous finally statement. When combined
* with `catch()`, `finally()` allows you to write code that is similar to the familiar
* synchronous catch/finally pair.
*
* Consider the following synchronous code:
Expand All @@ -103,14 +103,14 @@ public function otherwise(callable $onRejected): PromiseInterface;
*
* ```php
* return doSomething()
* ->otherwise('handleError')
* ->always('cleanup');
* ->catch('handleError')
* ->finally('cleanup');
* ```
*
* @param callable $onFulfilledOrRejected
* @return PromiseInterface
*/
public function always(callable $onFulfilledOrRejected): PromiseInterface;
public function finally(callable $onFulfilledOrRejected): PromiseInterface;

/**
* The `cancel()` method notifies the creator of the promise that there is no
Expand All @@ -122,4 +122,38 @@ public function always(callable $onFulfilledOrRejected): PromiseInterface;
* @return void
*/
public function cancel(): void;

/**
* [Deprecated] Registers a rejection handler for a promise.
*
* This method continues to exist only for BC reasons and to ease upgrading
* between versions. It is an alias for:
*
* ```php
* $promise->catch($onRejected);
* ```
*
* @param callable $onRejected
* @return PromiseInterface
* @deprecated 3.0.0 Use catch() instead
* @see self::catch()
*/
public function otherwise(callable $onRejected): PromiseInterface;

/**
* [Deprecated] Allows you to execute "cleanup" type tasks in a promise chain.
*
* This method continues to exist only for BC reasons and to ease upgrading
* between versions. It is an alias for:
*
* ```php
* $promise->finally($onFulfilledOrRejected);
* ```
*
* @param callable $onFulfilledOrRejected
* @return PromiseInterface
* @deprecated 3.0.0 Use finally() instead
* @see self::finally()
*/
public function always(callable $onFulfilledOrRejected): PromiseInterface;
}
30 changes: 29 additions & 1 deletion tests/PromiseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,31 @@ public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingPro
}

/** @test */
public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingPromiseWithCatchFollowers()
{
gc_collect_cycles();
$promise = new Promise(function () { });
$promise->catch(function () { });
unset($promise);

$this->assertSame(0, gc_collect_cycles());
}

/** @test */
public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingPromiseWithFinallyFollowers()
{
gc_collect_cycles();
$promise = new Promise(function () { });
$promise->finally(function () { });
unset($promise);

$this->assertSame(0, gc_collect_cycles());
}

/**
* @test
* @deprecated
*/
public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingPromiseWithOtherwiseFollowers()
{
gc_collect_cycles();
Expand All @@ -234,7 +259,10 @@ public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingPro
$this->assertSame(0, gc_collect_cycles());
}

/** @test */
/**
* @test
* @deprecated
*/
public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingPromiseWithAlwaysFollowers()
{
gc_collect_cycles();
Expand Down
Loading