Skip to content

Commit 68633ae

Browse files
committed
Added support for exception handlers that return promises.
Upgraded amp minimum version from 2.0 -> 2.1 to fix timer issue #243.
1 parent a67d336 commit 68633ae

File tree

3 files changed

+72
-7
lines changed

3 files changed

+72
-7
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
},
1818
"require-dev": {
1919
"phpunit/phpunit": "^4.8",
20-
"amphp/amp": "^2"
20+
"amphp/amp": "^2.1"
2121
},
2222
"autoload": {
2323
"files": [

src/retry.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
function retry($tries, callable $operation, callable $onError = null)
2323
{
2424
/** @var \Generator $generator */
25-
$generator = (static function () use ($tries, $operation, $onError) {
25+
$generator = (static function () use ($tries, $operation, $onError): \Generator {
26+
// Nothing to do if tries less than or equal to zero.
2627
if (($tries |= 0) <= $attempts = 0) {
2728
return;
2829
}
@@ -39,8 +40,14 @@ function retry($tries, callable $operation, callable $onError = null)
3940
throw new FailingTooHardException($attempts, $exception);
4041
}
4142

42-
if ($onError && $onError($exception) === false) {
43-
return;
43+
if ($onError) {
44+
if (($result = $onError($exception)) instanceof Promise) {
45+
$result = yield $result;
46+
}
47+
48+
if ($result === false) {
49+
return;
50+
}
4451
}
4552

4653
goto beginning;

test/RetryAsyncTest.php

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
namespace ScriptFUSIONTest\Retry;
33

44
use Amp\Delayed;
5+
use Amp\Promise;
6+
use Amp\Success;
57
use ScriptFUSION\Retry\FailingTooHardException;
68

79
final class RetryAsyncTest extends \PHPUnit_Framework_TestCase
@@ -94,7 +96,7 @@ public function testFailingTooHardAsync()
9496
}
9597

9698
/**
97-
* These that the error callback is called before each retry.
99+
* Tests that the error callback is called before each retry.
98100
*/
99101
public function testErrorCallbackAsync()
100102
{
@@ -120,13 +122,37 @@ public function testErrorCallbackAsync()
120122
}
121123

122124
/**
123-
* Tests that an error handler that returns false aborts retrying.
125+
* Tests that an error callback that returns a promise has its promise resolved.
126+
*/
127+
public function testPromiseErrorCallback()
128+
{
129+
$delay = 250; // Quarter of a second.
130+
$start = microtime(true);
131+
132+
try {
133+
\Amp\Promise\wait(
134+
\ScriptFUSION\Retry\retryAsync($tries = 3, static function () {
135+
throw new \DomainException;
136+
}, static function () use ($delay): Promise {
137+
return new Delayed($delay);
138+
})
139+
);
140+
} catch (FailingTooHardException $outerException) {
141+
self::assertInstanceOf(\DomainException::class, $outerException->getPrevious());
142+
}
143+
144+
self::assertTrue(isset($outerException));
145+
self::assertGreaterThan($start + $delay * ($tries - 1) / 1000, microtime(true));
146+
}
147+
148+
/**
149+
* Tests that when error handler that returns false, it aborts retrying.
124150
*/
125151
public function testErrorCallbackHaltAsync()
126152
{
127153
$invocations = 0;
128154

129-
\ScriptFUSION\Retry\retryAsync($tries = 2, static function () use (&$invocations) {
155+
\ScriptFUSION\Retry\retryAsync(2, static function () use (&$invocations) {
130156
++$invocations;
131157

132158
throw new \RuntimeException;
@@ -136,4 +162,36 @@ public function testErrorCallbackHaltAsync()
136162

137163
self::assertSame(1, $invocations);
138164
}
165+
166+
/**
167+
* Tests that when an error handler returns a promise that false, it aborts retrying.
168+
*/
169+
public function testPromiseErrorCallbackHaltAsync()
170+
{
171+
$invocations = 0;
172+
173+
\ScriptFUSION\Retry\retryAsync(2, static function () use (&$invocations) {
174+
++$invocations;
175+
176+
throw new \RuntimeException;
177+
}, static function (): Promise {
178+
return new Success(false);
179+
});
180+
181+
self::assertSame(1, $invocations);
182+
}
183+
184+
/**
185+
* Tests that the exception handler can throw an exception that will not be caught.
186+
*/
187+
public function testErrorCallbackCanThrow()
188+
{
189+
$this->setExpectedException(\LogicException::class);
190+
191+
\ScriptFUSION\Retry\retryAsync(2, static function () {
192+
throw new \RuntimeException;
193+
}, static function () {
194+
throw new \LogicException;
195+
});
196+
}
139197
}

0 commit comments

Comments
 (0)