mirror of
https://github.com/danog/amp.git
synced 2024-11-30 04:29:08 +01:00
Lots of changes ... see CHANGELOG for details
This commit is contained in:
parent
0d7ab5fc6c
commit
f32d088c23
39
CHANGELOG.md
39
CHANGELOG.md
@ -1,21 +1,36 @@
|
||||
### master
|
||||
|
||||
- Remove `Combinator` class in favor of combinator functions
|
||||
- Remove `Resolver` class
|
||||
- Remove superfluous Reactor function analogs
|
||||
- Add `Reactor::onError()` exception handling hook
|
||||
- Correctly exit `UvReactor` and `LibeventReactor` run loop when no outstanding watchers
|
||||
remain active
|
||||
- All `Promisor` implementations now require a `Reactor` constructor parameter.
|
||||
Previously these implementations would lazy-inject the global singleton event
|
||||
reactor instance if no reactor parameter was specified in the constructor.
|
||||
- Add optional boolean `Reactor::tick($noWait)` parameter
|
||||
**Additions**
|
||||
|
||||
- Added `Reactor::onError()` exception handling hook
|
||||
- Added optional boolean `$noWait` parameter to `Reactor::tick($noWait)`
|
||||
- Added `Amp\getReactor()` and `Amp\chooseReactor()` functions
|
||||
|
||||
**Bugfixes:**
|
||||
|
||||
- Correctly break out of the `NativeReactor` run loop immediately when
|
||||
`Reactor::stop()` invoked inside immediately watchers.
|
||||
`Reactor::stop()` invoked inside immediately watchers
|
||||
- Correctly exit `UvReactor` and `LibeventReactor` run loop when no outstanding
|
||||
watchers remain active
|
||||
|
||||
**Removals:**
|
||||
|
||||
- Removed `Combinator` class in favor of combinator functions
|
||||
- Removed `Resolver` class, use `GeneratorResolver` trait internally
|
||||
- `Promisor` implementations no longer have any knowledge of the event reactor.
|
||||
|
||||
**Deprecations:**
|
||||
|
||||
- Deprecated `Promise::wait()`. New code should use `Amp\wait()` to synchronously
|
||||
wait for promise completion
|
||||
- Deprecated `Amp\reactor()` function. New code should use `Amp\getReactor()`
|
||||
instead
|
||||
- The `ReactorFactory` class is deprecated and scheduled for removal. Please use
|
||||
the `Amp\getReactor()` function instead of `ReactorFactory::select()`
|
||||
|
||||
> **BC BREAKS:**
|
||||
|
||||
- All the changes listed above are BC breaks -- please update code accordingly :)
|
||||
- None
|
||||
|
||||
v0.14.0
|
||||
-------
|
||||
|
@ -39,15 +39,15 @@ class Failure implements Promise {
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for Future value resolution
|
||||
*
|
||||
* NOTE: because this object represents a failed Promise it will *always* immediately throw the
|
||||
* exception responsible for resolution failure.
|
||||
*
|
||||
* @throws \Exception
|
||||
* @return void
|
||||
* This method is deprecated. New code should use Amp\wait($promise) instead.
|
||||
*/
|
||||
public function wait() {
|
||||
trigger_error(
|
||||
'Amp\\Promise::wait() is deprecated and scheduled for removal. ' .
|
||||
'Please update code to use Amp\\wait($promise) instead.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
|
||||
throw $this->error;
|
||||
}
|
||||
}
|
||||
|
@ -3,21 +3,12 @@
|
||||
namespace Amp;
|
||||
|
||||
class Future implements Promisor, Promise {
|
||||
private $reactor;
|
||||
private $isWaiting = false;
|
||||
private $isResolved = false;
|
||||
private $watchers = [];
|
||||
private $whens = [];
|
||||
private $error;
|
||||
private $result;
|
||||
|
||||
/**
|
||||
* @param \Amp\Reactor $reactor
|
||||
*/
|
||||
public function __construct(Reactor $reactor) {
|
||||
$this->reactor = $reactor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the Promise placeholder for this deferred value
|
||||
*
|
||||
@ -64,32 +55,26 @@ class Future implements Promisor, Promise {
|
||||
}
|
||||
|
||||
/**
|
||||
* Block script execution indefinitely until the promise resolves
|
||||
*
|
||||
* @throws \Exception
|
||||
* @return mixed
|
||||
* This method is deprecated. New code should use Amp\wait($promise) instead.
|
||||
*/
|
||||
public function wait() {
|
||||
if ($this->error) {
|
||||
throw $this->error;
|
||||
} elseif ($this->isResolved) {
|
||||
return $this->result;
|
||||
}
|
||||
trigger_error(
|
||||
'Amp\\Promise::wait() is deprecated and scheduled for removal. ' .
|
||||
'Please update code to use Amp\\wait($promise) instead.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
|
||||
$resolvedError;
|
||||
$resolvedResult;
|
||||
|
||||
$this->whens[] = function($error, $result) use (&$resolvedError, &$resolvedResult) {
|
||||
$isWaiting = true;
|
||||
$resolvedError = $resolvedResult = null;
|
||||
$this->when(function($error, $result) use (&$isWaiting, &$resolvedError, &$resolvedResult) {
|
||||
$isWaiting = false;
|
||||
$resolvedError = $error;
|
||||
$resolvedResult = $result;
|
||||
$this->isWaiting = false;
|
||||
};
|
||||
|
||||
$this->isWaiting = true;
|
||||
while ($this->isWaiting) {
|
||||
$this->reactor->tick();
|
||||
});
|
||||
$reactor = getReactor();
|
||||
while ($isWaiting) {
|
||||
$reactor->tick();
|
||||
}
|
||||
|
||||
if ($resolvedError) {
|
||||
throw $resolvedError;
|
||||
}
|
||||
|
@ -13,11 +13,8 @@ class PrivateFuture implements Promisor {
|
||||
private $updater;
|
||||
private $promise;
|
||||
|
||||
/**
|
||||
* @param \Amp\Reactor $reactor
|
||||
*/
|
||||
public function __construct(Reactor $reactor) {
|
||||
$unresolved = new Unresolved($reactor);
|
||||
public function __construct() {
|
||||
$unresolved = new Unresolved;
|
||||
$resolver = function(\Exception $error = null, $result = null) {
|
||||
$this->resolve($error, $result); // bound to private Unresolved::resolve()
|
||||
};
|
||||
|
@ -41,12 +41,4 @@ interface Promise {
|
||||
* @return self
|
||||
*/
|
||||
public function watch(callable $func);
|
||||
|
||||
/**
|
||||
* Block script execution indefinitely until the promise resolves
|
||||
*
|
||||
* In the event of promise failure, implementations MUST throw the Exception object used to
|
||||
* fail the Promise. Upon success this method MUST return the successfully resolved value.
|
||||
*/
|
||||
public function wait();
|
||||
}
|
||||
|
@ -2,26 +2,19 @@
|
||||
|
||||
namespace Amp;
|
||||
|
||||
/**
|
||||
* The event reactor is a truly global thing in single-threaded code. Applications should use
|
||||
* a single reactor per thread. Accidentally using multiple reactors can lead to all manner of
|
||||
* hard-to-debug problems. Should you almost always avoid static and singletons? Yes, and if you
|
||||
* abuse this static factory method it's your fault. However, there is nothing wrong with
|
||||
* asking for a Reactor instance in your code and using lazy injection via this method if it's
|
||||
* not provided.
|
||||
*
|
||||
* DO NOT instantiate multiple event loops in your PHP application!
|
||||
*/
|
||||
class ReactorFactory {
|
||||
private static $reactor;
|
||||
|
||||
/**
|
||||
* Select a global event reactor based on the current environment
|
||||
*
|
||||
* @param callable $factory An optional factory callable to generate the shared reactor yourself
|
||||
* @return \Amp\Reactor
|
||||
* This method is deprecated. New code should use Amp\getReactor() instead.
|
||||
*/
|
||||
public static function select(callable $factory = null) {
|
||||
trigger_error(
|
||||
'Amp\\ReactorFactory is deprecated and scheduled for removal. ' .
|
||||
'Please update code to use the Amp\\getReactor() function instead.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
|
||||
if (self::$reactor) {
|
||||
return self::$reactor;
|
||||
} elseif ($factory) {
|
||||
|
@ -39,14 +39,15 @@ class Success implements Promise {
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for Future value resolution
|
||||
*
|
||||
* NOTE: because this object represents a successfully resolved Promise it will *always* return
|
||||
* the resolved result immediately.
|
||||
*
|
||||
* @return mixed
|
||||
* This method is deprecated. New code should use Amp\wait($promise) instead.
|
||||
*/
|
||||
public function wait() {
|
||||
trigger_error(
|
||||
'Amp\\Promise::wait() is deprecated and scheduled for removal. ' .
|
||||
'Please update code to use Amp\\wait($promise) instead.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
|
||||
return $this->result;
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ namespace Amp;
|
||||
* the Promisor that created it.
|
||||
*/
|
||||
class Unresolved implements Promise {
|
||||
private $reactor;
|
||||
private $isWaiting = false;
|
||||
private $isResolved = false;
|
||||
private $watchers = [];
|
||||
@ -15,13 +14,6 @@ class Unresolved implements Promise {
|
||||
private $error;
|
||||
private $result;
|
||||
|
||||
/**
|
||||
* @param \Amp\Reactor $reactor
|
||||
*/
|
||||
public function __construct(Reactor $reactor) {
|
||||
$this->reactor = $reactor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the $func callback when the promise resolves (whether successful or not)
|
||||
*
|
||||
@ -53,32 +45,26 @@ class Unresolved implements Promise {
|
||||
}
|
||||
|
||||
/**
|
||||
* Block script execution indefinitely until the promise resolves
|
||||
*
|
||||
* @throws \Exception
|
||||
* @return mixed
|
||||
* This method is deprecated. New code should use Amp\wait($promise) instead.
|
||||
*/
|
||||
public function wait() {
|
||||
if ($this->error) {
|
||||
throw $error;
|
||||
} elseif ($this->isResolved) {
|
||||
return $this->result;
|
||||
}
|
||||
trigger_error(
|
||||
'Amp\\Promise::wait() is deprecated and scheduled for removal. ' .
|
||||
'Please update code to use Amp\\wait($promise) instead.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
|
||||
$resolvedError;
|
||||
$resolvedResult;
|
||||
|
||||
$this->whens[] = function($error, $result) use (&$resolvedError, &$resolvedResult) {
|
||||
$isWaiting = true;
|
||||
$resolvedError = $resolvedResult = null;
|
||||
$this->when(function($error, $result) use (&$isWaiting, &$resolvedError, &$resolvedResult) {
|
||||
$isWaiting = false;
|
||||
$resolvedError = $error;
|
||||
$resolvedResult = $result;
|
||||
$this->isWaiting = false;
|
||||
};
|
||||
|
||||
$this->isWaiting = true;
|
||||
while ($this->isWaiting) {
|
||||
$this->reactor->tick();
|
||||
});
|
||||
$reactor = getReactor();
|
||||
while ($isWaiting) {
|
||||
$reactor->tick();
|
||||
}
|
||||
|
||||
if ($resolvedError) {
|
||||
throw $resolvedError;
|
||||
}
|
||||
|
@ -3,16 +3,218 @@
|
||||
namespace Amp;
|
||||
|
||||
/**
|
||||
* Get a singleton event reactor instance
|
||||
* Get the global singleton event reactor instance
|
||||
*
|
||||
* Note that the $factory callable is only invoked if no global reactor has yet been initialized.
|
||||
*
|
||||
* @param callable $factory Optional factory callable for initializing a reactor
|
||||
* @return \Amp\Reactor
|
||||
* @param bool $forceNew If true return a new Reactor instance (but don't store it for future use)
|
||||
* @return Reactor
|
||||
*/
|
||||
function reactor(callable $factory = null) {
|
||||
function getReactor($forceNew = false) {
|
||||
static $reactor;
|
||||
return ($reactor = $reactor ?: ReactorFactory::select($factory));
|
||||
|
||||
if ($forceNew) {
|
||||
return chooseReactor();
|
||||
} elseif (empty($reactor)) {
|
||||
return $reactor = chooseReactor();
|
||||
} else {
|
||||
return $reactor;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the most appropriate event reactor given the current execution environment
|
||||
*
|
||||
* @return LibeventReactor|NativeReactor|UvReactor
|
||||
*/
|
||||
function chooseReactor() {
|
||||
if (extension_loaded('uv')) {
|
||||
return new UvReactor;
|
||||
} elseif (extension_loaded('libevent')) {
|
||||
return new LibeventReactor;
|
||||
} else {
|
||||
return new NativeReactor;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start an event reactor and assume program flow control
|
||||
*
|
||||
* @param callable $onStart Optional callback to invoke immediately upon reactor start
|
||||
* @return void
|
||||
*/
|
||||
function run(callable $onStart = null) {
|
||||
return getReactor()->run($onStart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a single event loop iteration
|
||||
*
|
||||
* @param bool $noWait
|
||||
* @return void
|
||||
*/
|
||||
function tick($noWait = false) {
|
||||
return getReactor()->tick($noWait);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the event reactor
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function stop() {
|
||||
return getReactor()->stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a callback for immediate invocation in the next event loop iteration
|
||||
*
|
||||
* Watchers registered using this function will be automatically garbage collected after execution.
|
||||
*
|
||||
* @param callable $func Any valid PHP callable
|
||||
* @return int Returns the unique watcher ID for disable/enable/cancel
|
||||
*/
|
||||
function immediately(callable $func) {
|
||||
return getReactor()->immediately($func);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a callback to execute once
|
||||
*
|
||||
* Watchers registered using this function will be automatically garbage collected after execution.
|
||||
*
|
||||
* @param callable $func Any valid PHP callable
|
||||
* @param int $msDelay The delay in milliseconds before the callback will trigger (may be zero)
|
||||
* @return int Returns the unique watcher ID for disable/enable/cancel
|
||||
*/
|
||||
function once(callable $func, $msDelay) {
|
||||
return getReactor()->once($func, $msDelay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a recurring callback to execute every $interval seconds until cancelled
|
||||
*
|
||||
* IMPORTANT: Watchers registered using this function must be manually cleared using cancel() to
|
||||
* free the associated memory. Failure to cancel repeating watchers (even if disable() is used)
|
||||
* will lead to memory leaks.
|
||||
*
|
||||
* @param callable $func Any valid PHP callable
|
||||
* @param int $msDelay The delay in milliseconds in-between callback invocations (may be zero)
|
||||
* @return int Returns the unique watcher ID for disable/enable/cancel
|
||||
*/
|
||||
function repeat(callable $func, $msDelay) {
|
||||
return getReactor()->repeat($func, $msDelay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule an event to trigger once at the specified time
|
||||
*
|
||||
* Watchers registered using this function will be automatically garbage collected after execution.
|
||||
*
|
||||
* @param callable $func Any valid PHP callable
|
||||
* @param string $timeString Any string that can be parsed by strtotime() and is in the future
|
||||
* @return int Returns the unique watcher ID for disable/enable/cancel
|
||||
*/
|
||||
function at(callable $func, $timeString) {
|
||||
return getReactor()->at($func, $timeString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable a disabled timer or stream IO watcher
|
||||
*
|
||||
* Calling enable() on an already-enabled watcher will have no effect.
|
||||
*
|
||||
* @param int $watcherId
|
||||
* @return void
|
||||
*/
|
||||
function enable($watcherId) {
|
||||
return getReactor()->enable($watcherId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporarily disable (but don't cancel) an existing timer/stream watcher
|
||||
*
|
||||
* Calling disable() on a nonexistent or previously-disabled watcher will have no effect.
|
||||
*
|
||||
* NOTE: Disabling a repeating or stream watcher is not sufficient to free associated resources.
|
||||
* When the watcher is no longer needed applications must still use cancel() to clear related
|
||||
* memory and avoid leaks.
|
||||
*
|
||||
* @param int $watcherId
|
||||
* @return void
|
||||
*/
|
||||
function disable($watcherId) {
|
||||
return getReactor()->disable($watcherId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel an existing timer/stream watcher
|
||||
*
|
||||
* Calling cancel() on a non-existent watcher will have no effect.
|
||||
*
|
||||
* @param int $watcherId
|
||||
* @return void
|
||||
*/
|
||||
function cancel($watcherId) {
|
||||
return getReactor()->cancel($watcherId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch a stream IO resource for readable data and trigger the specified callback when actionable
|
||||
*
|
||||
* IMPORTANT: Watchers registered using this function must be manually cleared using cancel() to
|
||||
* free the associated memory. Failure to cancel repeating watchers (even if disable() is used)
|
||||
* will lead to memory leaks.
|
||||
*
|
||||
* @param resource $stream A stream resource to watch for readable data
|
||||
* @param callable $func Any valid PHP callable
|
||||
* @param bool $enableNow Should the watcher be enabled now or held for later use?
|
||||
* @return int Returns the unique watcher ID for disable/enable/cancel
|
||||
*/
|
||||
function onReadable($stream, callable $func, $enableNow = true) {
|
||||
return getReactor()->onReadable($stream, $func, $enableNow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch a stream IO resource for writability and trigger the specified callback when actionable
|
||||
*
|
||||
* NOTE: Sockets are essentially "always writable" (as long as their write buffer is not full).
|
||||
* Therefore, it's critical that applications disable or cancel write watchers as soon as all data
|
||||
* is written or the watcher will trigger endlessly and hammer the CPU.
|
||||
*
|
||||
* IMPORTANT: Watchers registered using this function must be manually cleared using cancel() to
|
||||
* free the associated memory. Failure to cancel repeating watchers (even if disable() is used)
|
||||
* will lead to memory leaks.
|
||||
*
|
||||
* @param resource $stream A stream resource to watch for writable data
|
||||
* @param callable $func Any valid PHP callable
|
||||
* @param bool $enableNow Should the watcher be enabled now or held for later use?
|
||||
* @return int Returns the unique watcher ID for disable/enable/cancel
|
||||
*/
|
||||
function onWritable($stream, callable $func, $enableNow = true) {
|
||||
return getReactor()->onWritable($stream, $func, $enableNow);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* React to process control signals
|
||||
*
|
||||
* @param int $signo The signal number to watch for
|
||||
* @param callable $onSignal
|
||||
* @throws \RuntimeException if the current environment cannot support signal handling
|
||||
* @return int Returns a unique integer watcher ID
|
||||
*/
|
||||
function onSignal($signo, callable $onSignal) {
|
||||
/**
|
||||
* @var $reactor \Amp\SignalReactor
|
||||
*/
|
||||
$reactor = getReactor();
|
||||
if ($reactor instanceof SignalReactor) {
|
||||
return $reactor->onSignal($signo, $onSignal);
|
||||
} else {
|
||||
throw new \RuntimeException(
|
||||
'Your PHP environment does not support signal handling. Please install pecl/libevent or the php-uv extension'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -20,18 +222,17 @@ function reactor(callable $factory = null) {
|
||||
* the resulting Promise succeeds with an array matching keys from the input array
|
||||
* to their resolved values.
|
||||
*
|
||||
* @param array[\Amp\Promise] $promises
|
||||
* @param \Amp\Reactor $reactor
|
||||
* @return \Amp\Promise
|
||||
* @param array[Promise] $promises
|
||||
* @return Promise
|
||||
*/
|
||||
function all(array $promises, Reactor $reactor = null) {
|
||||
function all(array $promises) {
|
||||
if (empty($promises)) {
|
||||
return new Success([]);
|
||||
}
|
||||
|
||||
$results = [];
|
||||
$remaining = count($promises);
|
||||
$promisor = new Future($reactor ?: reactor());
|
||||
$promisor = new Future;
|
||||
$isResolved = false;
|
||||
|
||||
foreach ($promises as $key => $resolvable) {
|
||||
@ -78,11 +279,10 @@ function all(array $promises, Reactor $reactor = null) {
|
||||
* The individual keys in the resulting arrays are preserved from the initial Promise array
|
||||
* passed to the function for evaluation.
|
||||
*
|
||||
* @param array[\Amp\Promise] $promises
|
||||
* @param \Amp\Reactor $reactor
|
||||
* @return \Amp\Promise
|
||||
* @param array[Promise] $promises
|
||||
* @return Promise
|
||||
*/
|
||||
function some(array $promises, Reactor $reactor = null) {
|
||||
function some(array $promises) {
|
||||
if (empty($promises)) {
|
||||
return new Failure(new \LogicException(
|
||||
'No promises or values provided for resolution'
|
||||
@ -91,7 +291,7 @@ function some(array $promises, Reactor $reactor = null) {
|
||||
|
||||
$results = $errors = [];
|
||||
$remaining = count($promises);
|
||||
$promisor = new Future($reactor ?: reactor());
|
||||
$promisor = new Future;
|
||||
|
||||
foreach ($promises as $key => $resolvable) {
|
||||
if (!$resolvable instanceof Promise) {
|
||||
@ -130,11 +330,10 @@ function some(array $promises, Reactor $reactor = null) {
|
||||
* This function is the same as some() with the notable exception that it will never fail even
|
||||
* if all promises in the array resolve unsuccessfully.
|
||||
*
|
||||
* @param array $promises
|
||||
* @param \Amp\Reactor $reactor
|
||||
* @return \Amp\Promise
|
||||
* @param array[Promise] $promises
|
||||
* @return Promise
|
||||
*/
|
||||
function any(array $promises, Reactor $reactor = null) {
|
||||
function any(array $promises) {
|
||||
if (empty($promises)) {
|
||||
return new Success([], []);
|
||||
}
|
||||
@ -142,7 +341,7 @@ function any(array $promises, Reactor $reactor = null) {
|
||||
$results = [];
|
||||
$errors = [];
|
||||
$remaining = count($promises);
|
||||
$promisor = new Future($reactor ?: reactor());
|
||||
$promisor = new Future;
|
||||
|
||||
foreach ($promises as $key => $resolvable) {
|
||||
if (!$resolvable instanceof Promise) {
|
||||
@ -173,11 +372,10 @@ function any(array $promises, Reactor $reactor = null) {
|
||||
* Resolves with the first successful Promise value. The resulting Promise will only fail if all
|
||||
* Promise values in the group fail or if the initial Promise array is empty.
|
||||
*
|
||||
* @param array[\Amp\Promise] $promises
|
||||
* @param \Amp\Reactor $reactor
|
||||
* @return \Amp\Promise
|
||||
* @param array[Promise] $promises
|
||||
* @return Promise
|
||||
*/
|
||||
function first(array $promises, Reactor $reactor = null) {
|
||||
function first(array $promises) {
|
||||
if (empty($promises)) {
|
||||
return new Failure(new \LogicException(
|
||||
'No promises or values provided for resolution'
|
||||
@ -186,7 +384,7 @@ function first(array $promises, Reactor $reactor = null) {
|
||||
|
||||
$remaining = count($promises);
|
||||
$isComplete = false;
|
||||
$promisor = new Future($reactor ?: reactor());
|
||||
$promisor = new Future;
|
||||
|
||||
foreach ($promises as $resolvable) {
|
||||
if (!$resolvable instanceof Promise) {
|
||||
@ -217,19 +415,18 @@ function first(array $promises, Reactor $reactor = null) {
|
||||
/**
|
||||
* Map promised future values using the specified functor
|
||||
*
|
||||
* @param array $promises
|
||||
* @param array[Promise] $promises
|
||||
* @param callable $functor
|
||||
* @param \Amp\Reactor $reactor
|
||||
* @return \Amp\Promise
|
||||
* @return Promise
|
||||
*/
|
||||
function map(array $promises, callable $functor, Reactor $reactor = null) {
|
||||
function map(array $promises, callable $functor) {
|
||||
if (empty($promises)) {
|
||||
return new Success([]);
|
||||
}
|
||||
|
||||
$results = [];
|
||||
$remaining = count($promises);
|
||||
$promisor = new Future($reactor ?: reactor());
|
||||
$promisor = new Future;
|
||||
|
||||
foreach ($promises as $key => $resolvable) {
|
||||
$promise = ($resolvable instanceof Promise) ? $resolvable : new Success($resolvable);
|
||||
@ -267,19 +464,18 @@ function map(array $promises, callable $functor, Reactor $reactor = null) {
|
||||
* If the functor returns a truthy value the resolved promise result is retained, otherwise it is
|
||||
* discarded. Array keys are retained for any results not filtered out by the functor.
|
||||
*
|
||||
* @param array $promises
|
||||
* @param array[Promise] $promises
|
||||
* @param callable $functor
|
||||
* @param \Amp\Reactor $reactor
|
||||
* @return \Amp\Promise
|
||||
* @return Promise
|
||||
*/
|
||||
function filter(array $promises, callable $functor, Reactor $reactor = null) {
|
||||
function filter(array $promises, callable $functor) {
|
||||
if (empty($promises)) {
|
||||
return new Success([]);
|
||||
}
|
||||
|
||||
$results = [];
|
||||
$remaining = count($promises);
|
||||
$promisor = new Future($reactor ?: reactor());
|
||||
$promisor = new Future;
|
||||
|
||||
foreach ($promises as $key => $resolvable) {
|
||||
$promise = ($resolvable instanceof Promise) ? $resolvable : new Success($resolvable);
|
||||
@ -312,245 +508,73 @@ function filter(array $promises, callable $functor, Reactor $reactor = null) {
|
||||
}
|
||||
|
||||
/**
|
||||
* A co-routine to resolve Generators
|
||||
* Block script execution indefinitely until the specified Promise resolves
|
||||
*
|
||||
* Returns a promise that resolves when the generator completes. The final value
|
||||
* yielded by the generator is used to resolve the returned Promise on success.
|
||||
* In the event of promise failure this method will throw the exception responsible for the failure.
|
||||
* Otherwise the promise's resolved value is returned.
|
||||
*
|
||||
* Example:
|
||||
* If the optional event reactor instance is not specified then the global default event reactor
|
||||
* is used. Applications should be very careful to avoid instantiating multiple event reactors as
|
||||
* this can lead to hard-to-debug failures. If the async value producer uses a different event
|
||||
* reactor instance from that specified in this method the wait() call will never return.
|
||||
*
|
||||
* function anotherGenerator() {
|
||||
* // wait 100 milliseconds before proceeding
|
||||
* yield 'wait' => 100;
|
||||
* yield 1;
|
||||
* }
|
||||
*
|
||||
* $generator = function() {
|
||||
* $a = (yield 2);
|
||||
* $b = (yield new Success(21));
|
||||
* $c = (yield anotherGenerator());
|
||||
* yield $a * $b * $c;
|
||||
* };
|
||||
*
|
||||
* $result = resolve($generator())->wait();
|
||||
* var_dump($result); // int(42)
|
||||
*
|
||||
* @param \Generator $gen
|
||||
* @param \Amp\Reactor $reactor
|
||||
* @return \Amp\Promise
|
||||
* @param Promise $promise A promise on which to wait for resolution
|
||||
* @param Reactor $reactor An optional event reactor instance
|
||||
* @throws \Exception
|
||||
* @return mixed
|
||||
*/
|
||||
function resolve(\Generator $gen, Reactor $reactor = null) {
|
||||
$promisor = new Future($reactor ?: reactor());
|
||||
__advanceGenerator($reactor, $gen, $promisor);
|
||||
function wait(Promise $promise, Reactor $reactor = null) {
|
||||
$isWaiting = true;
|
||||
$resolvedError = null;
|
||||
$resolvedResult = null;
|
||||
|
||||
return $promisor;
|
||||
}
|
||||
$promise->when(function($error, $result) use (&$isWaiting, &$resolvedError, &$resolvedResult) {
|
||||
$isWaiting = false;
|
||||
$resolvedError = $error;
|
||||
$resolvedResult = $result;
|
||||
});
|
||||
|
||||
function __advanceGenerator(Reactor $reactor, \Generator $gen, Promisor $promisor, $previous = null) {
|
||||
try {
|
||||
if ($gen->valid()) {
|
||||
$key = $gen->key();
|
||||
$current = $gen->current();
|
||||
$promiseStruct = __promisifyGeneratorYield($reactor, $key, $current);
|
||||
$reactor->immediately(function() use ($reactor, $gen, $promisor, $promiseStruct) {
|
||||
list($promise, $noWait) = $promiseStruct;
|
||||
if ($noWait) {
|
||||
__sendToGenerator($reactor, $gen, $promisor, $error = null, $result = null);
|
||||
} else {
|
||||
$promise->when(function($error, $result) use ($reactor, $gen, $promisor) {
|
||||
__sendToGenerator($reactor, $gen, $promisor, $error, $result);
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$promisor->succeed($previous);
|
||||
}
|
||||
} catch (\Exception $error) {
|
||||
$promisor->fail($error);
|
||||
$reactor = $reactor ?: getReactor();
|
||||
while ($isWaiting) {
|
||||
$reactor->tick();
|
||||
}
|
||||
|
||||
if ($resolvedError) {
|
||||
throw $resolvedError;
|
||||
}
|
||||
|
||||
return $resolvedResult;
|
||||
}
|
||||
|
||||
function __promisifyGeneratorYield(Reactor $reactor, $key, $current) {
|
||||
$noWait = false;
|
||||
// === DEPRECATED FUNCTIONS ========================================================================
|
||||
|
||||
if ($key === (string) $key) {
|
||||
goto explicit_key;
|
||||
/**
|
||||
* Get the global singleton event reactor instance
|
||||
*
|
||||
* Note that the $factory callable is only invoked if no global reactor has yet been initialized.
|
||||
*
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* === THIS FUNCTION IS DEPRECATED ===
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
*
|
||||
* @param callable $factory Optional factory callable for initializing a reactor
|
||||
* @return Reactor
|
||||
*/
|
||||
function reactor(callable $factory = null) {
|
||||
static $reactor;
|
||||
|
||||
$msg = 'This function is deprecated and scheduled for removal. Please use Amp\\getReactor()';
|
||||
trigger_error($msg, E_USER_DEPRECATED);
|
||||
|
||||
if ($reactor) {
|
||||
return $reactor;
|
||||
} elseif ($factory) {
|
||||
return ($reactor = $factory());
|
||||
} elseif (extension_loaded('uv')) {
|
||||
return ($reactor = new UvReactor);
|
||||
} elseif (extension_loaded('libevent')) {
|
||||
return ($reactor = new LibeventReactor);
|
||||
} else {
|
||||
goto implicit_key;
|
||||
}
|
||||
|
||||
explicit_key: {
|
||||
$key = strtolower($key);
|
||||
if ($key[0] === YieldCommands::NOWAIT_PREFIX) {
|
||||
$noWait = true;
|
||||
$key = substr($key, 1);
|
||||
}
|
||||
|
||||
switch ($key) {
|
||||
case YieldCommands::ALL:
|
||||
// fallthrough
|
||||
case YieldCommands::ANY:
|
||||
// fallthrough
|
||||
case YieldCommands::SOME:
|
||||
if (is_array($current)) {
|
||||
goto combinator;
|
||||
} else {
|
||||
$promise = new Failure(new \DomainException(
|
||||
sprintf('"%s" yield command expects array; %s yielded', $key, gettype($current))
|
||||
));
|
||||
goto return_struct;
|
||||
}
|
||||
case YieldCommands::WAIT:
|
||||
goto wait;
|
||||
case YieldCommands::IMMEDIATELY:
|
||||
goto immediately;
|
||||
case YieldCommands::ONCE:
|
||||
// fallthrough
|
||||
case YieldCommands::REPEAT:
|
||||
goto schedule;
|
||||
case YieldCommands::ON_READABLE:
|
||||
$ioWatchMethod = 'onReadable';
|
||||
goto stream_io_watcher;
|
||||
case YieldCommands::ON_WRITABLE:
|
||||
$ioWatchMethod = 'onWritable';
|
||||
goto stream_io_watcher;
|
||||
case YieldCommands::ENABLE:
|
||||
// fallthrough
|
||||
case YieldCommands::DISABLE:
|
||||
// fallthrough
|
||||
case YieldCommands::CANCEL:
|
||||
goto watcher_control;
|
||||
case YieldCommands::NOWAIT:
|
||||
$noWait = true;
|
||||
goto implicit_key;
|
||||
default:
|
||||
$promise = new Failure(new \DomainException(
|
||||
sprintf('Unknown or invalid yield key: "%s"', $key)
|
||||
));
|
||||
goto return_struct;
|
||||
}
|
||||
}
|
||||
|
||||
implicit_key: {
|
||||
if ($current instanceof Promise) {
|
||||
$promise = $current;
|
||||
} elseif ($current instanceof \Generator) {
|
||||
$promise = resolve($reactor, $current);
|
||||
} elseif (is_array($current)) {
|
||||
$key = YieldCommands::ALL;
|
||||
goto combinator;
|
||||
} else {
|
||||
$promise = new Success($current);
|
||||
}
|
||||
|
||||
goto return_struct;
|
||||
}
|
||||
|
||||
combinator: {
|
||||
$promises = [];
|
||||
foreach ($current as $index => $element) {
|
||||
if ($element instanceof Promise) {
|
||||
$promise = $element;
|
||||
} elseif ($element instanceof \Generator) {
|
||||
$promise = resolve($element, $reactor);
|
||||
} else {
|
||||
$promise = new Success($element);
|
||||
}
|
||||
|
||||
$promises[$index] = $promise;
|
||||
}
|
||||
|
||||
$combinatorFunction = __NAMESPACE__ . "\\{$key}";
|
||||
$promise = $combinatorFunction($promises, $reactor);
|
||||
|
||||
goto return_struct;
|
||||
}
|
||||
|
||||
immediately: {
|
||||
if (is_callable($current)) {
|
||||
$watcherId = $reactor->immediately($current);
|
||||
$promise = new Success($watcherId);
|
||||
} else {
|
||||
$promise = new Failure(new \DomainException(
|
||||
sprintf(
|
||||
'"%s" yield command requires callable; %s provided',
|
||||
$key,
|
||||
gettype($current)
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
goto return_struct;
|
||||
}
|
||||
|
||||
schedule: {
|
||||
if (is_array($current) && isset($current[0], $current[1]) && is_callable($current[0])) {
|
||||
list($func, $msDelay) = $current;
|
||||
$watcherId = $reactor->{$key}($func, $msDelay);
|
||||
$promise = new Success($watcherId);
|
||||
} else {
|
||||
$promise = new Failure(new \DomainException(
|
||||
sprintf(
|
||||
'"%s" yield command requires [callable $func, int $msDelay]; %s provided',
|
||||
$key,
|
||||
gettype($current)
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
goto return_struct;
|
||||
}
|
||||
|
||||
stream_io_watcher: {
|
||||
if (is_array($current) && isset($current[0], $current[1]) && is_callable($current[1])) {
|
||||
list($stream, $func, $enableNow) = $current;
|
||||
$watcherId = $reactor->{$ioWatchMethod}($stream, $func, $enableNow);
|
||||
$promise = new Success($watcherId);
|
||||
} else {
|
||||
$promise = new Failure(new \DomainException(
|
||||
sprintf(
|
||||
'"%s" yield command requires [resource $stream, callable $func, bool $enableNow]; %s provided',
|
||||
$key,
|
||||
gettype($current)
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
goto return_struct;
|
||||
}
|
||||
|
||||
wait: {
|
||||
$promisor = new Future($reactor);
|
||||
$reactor->once(function() use ($promisor) {
|
||||
$promisor->succeed();
|
||||
}, (int) $current);
|
||||
|
||||
$promise = $promisor;
|
||||
|
||||
goto return_struct;
|
||||
}
|
||||
|
||||
watcher_control: {
|
||||
$reactor->{$key}($current);
|
||||
$promise = new Success;
|
||||
|
||||
goto return_struct;
|
||||
}
|
||||
|
||||
return_struct: {
|
||||
return [$promise, $noWait];
|
||||
}
|
||||
}
|
||||
|
||||
function __sendToGenerator(Reactor $reactor, \Generator $gen, Promisor $promisor, \Exception $error = null, $result = null) {
|
||||
try {
|
||||
if ($error) {
|
||||
$gen->throw($error);
|
||||
} else {
|
||||
$gen->send($result);
|
||||
}
|
||||
__advanceGenerator($reactor, $gen, $promisor, $result);
|
||||
} catch (\Exception $error) {
|
||||
$promisor->fail($error);
|
||||
return ($reactor = new NativeReactor);
|
||||
}
|
||||
}
|
||||
|
@ -14,14 +14,4 @@ class FailureTest extends \PHPUnit_Framework_TestCase {
|
||||
$this->assertSame($exception, $error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
* @expectedExceptionMessage test
|
||||
*/
|
||||
public function testWaitThrowsImmediately() {
|
||||
$exception = new \RuntimeException('test');
|
||||
$failure = new Failure($exception);
|
||||
$failure->wait();
|
||||
}
|
||||
}
|
||||
|
@ -1,311 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Test;
|
||||
|
||||
use Amp\Success;
|
||||
use Amp\Failure;
|
||||
use Amp\NativeReactor;
|
||||
|
||||
class FunctionsTest extends \PHPUnit_Framework_TestCase {
|
||||
public function testAllResolvesWithArrayOfResults() {
|
||||
$promises = [
|
||||
'r1' => new Success(42),
|
||||
'r2' => new Success(41),
|
||||
];
|
||||
|
||||
$reactor = new NativeReactor;
|
||||
$expected = ['r1' => 42, 'r2' => 41];
|
||||
$results = \Amp\all($promises, $reactor)->wait();
|
||||
$this->assertSame($expected, $results);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
* @expectedExceptionMessage zanzibar
|
||||
*/
|
||||
public function testAllThrowsIfAnyIndividualPromiseFails() {
|
||||
$exception = new \RuntimeException('zanzibar');
|
||||
$promises = [
|
||||
'r1' => new Success(42),
|
||||
'r2' => new Failure($exception),
|
||||
'r3' => new Success(40),
|
||||
];
|
||||
|
||||
$reactor = new NativeReactor;
|
||||
$results = \Amp\all($promises, $reactor)->wait();
|
||||
}
|
||||
|
||||
public function testSomeReturnsArrayOfErrorsAndResults() {
|
||||
$exception = new \RuntimeException('zanzibar');
|
||||
$promises = [
|
||||
'r1' => new Success(42),
|
||||
'r2' => new Failure($exception),
|
||||
'r3' => new Success(40),
|
||||
];
|
||||
|
||||
$reactor = new NativeReactor;
|
||||
list($errors, $results) = \Amp\some($promises, $reactor)->wait();
|
||||
|
||||
$this->assertSame(['r2' => $exception], $errors);
|
||||
$this->assertSame(['r1' => 42, 'r3' => 40], $results);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
* @expectedExceptionMessage All promises failed
|
||||
*/
|
||||
public function testSomeThrowsIfNoPromisesResolveSuccessfully() {
|
||||
$promises = [
|
||||
'r1' => new Failure(new \RuntimeException),
|
||||
'r2' => new Failure(new \RuntimeException),
|
||||
];
|
||||
$reactor = new NativeReactor;
|
||||
list($errors, $results) = \Amp\some($promises, $reactor)->wait();
|
||||
}
|
||||
|
||||
public function testResolveResolvesGeneratorResult() {
|
||||
$gen = function() {
|
||||
$a = (yield 21);
|
||||
$b = (yield new Success(2));
|
||||
yield ($a * $b);
|
||||
};
|
||||
|
||||
$reactor = new NativeReactor;
|
||||
$promise = \Amp\resolve($gen(), $reactor);
|
||||
$expected = 42;
|
||||
$actual = $promise->wait();
|
||||
$this->assertSame($expected, $actual);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// --- resolve() tests ------------------------------ //
|
||||
|
||||
public function testResolve() {
|
||||
$gen = function() { yield 42; };
|
||||
|
||||
$reactor = new NativeReactor;
|
||||
$result = \Amp\resolve($gen(), $reactor)->wait();
|
||||
$this->assertSame(42, $result);
|
||||
}
|
||||
|
||||
public function testResolvedValueEqualsFinalYield() {
|
||||
$gen = function() {
|
||||
$a = (yield 21);
|
||||
$b = (yield new Success(2));
|
||||
yield ($a * $b);
|
||||
};
|
||||
|
||||
$expected = 42;
|
||||
$reactor = new NativeReactor;
|
||||
$actual = \Amp\resolve($gen(), $reactor)->wait();
|
||||
$this->assertSame($expected, $actual);
|
||||
}
|
||||
|
||||
public function testFutureErrorsAreThrownIntoGenerator() {
|
||||
$gen = function() {
|
||||
$a = (yield 21);
|
||||
$b = 1;
|
||||
try {
|
||||
yield new Failure(new \Exception('test'));
|
||||
$this->fail('Code path should not be reached');
|
||||
} catch (\Exception $e) {
|
||||
$this->assertSame('test', $e->getMessage());
|
||||
$b = 2;
|
||||
}
|
||||
|
||||
yield ($a * $b);
|
||||
};
|
||||
|
||||
$expected = 42;
|
||||
$reactor = new NativeReactor;
|
||||
$actual = \Amp\resolve($gen(), $reactor)->wait();
|
||||
$this->assertSame($expected, $actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Exception
|
||||
* @expectedExceptionMessage When in the chronicle of wasted time
|
||||
*/
|
||||
public function testUncaughtGeneratorExceptionFailsResolverPromise() {
|
||||
$gen = function() {
|
||||
yield;
|
||||
throw new \Exception('When in the chronicle of wasted time');
|
||||
yield;
|
||||
};
|
||||
|
||||
$reactor = new NativeReactor;
|
||||
\Amp\resolve($gen(), $reactor)->wait();
|
||||
}
|
||||
|
||||
public function testImplicitAllCombinatorResolution() {
|
||||
$gen = function() {
|
||||
list($a, $b) = (yield [new Success(21), new Success(2)]);
|
||||
yield ($a * $b);
|
||||
};
|
||||
|
||||
$reactor = new NativeReactor;
|
||||
$result = \Amp\resolve($gen(), $reactor)->wait();
|
||||
$this->assertSame(42, $result);
|
||||
}
|
||||
|
||||
public function testImplicitAllCombinatorResolutionWithNonPromises() {
|
||||
$gen = function() {
|
||||
list($a, $b, $c) = (yield [new Success(21), new Success(2), 10]);
|
||||
yield ($a * $b * $c);
|
||||
};
|
||||
|
||||
$reactor = new NativeReactor;
|
||||
$result = \Amp\resolve($gen(), $reactor)->wait();
|
||||
$this->assertSame(420, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Exception
|
||||
* @expectedExceptionMessage When in the chronicle of wasted time
|
||||
*/
|
||||
public function testImplicitCombinatorResolutionThrowsIfAnyOnePromiseFails() {
|
||||
$gen = function() {
|
||||
list($a, $b) = (yield [
|
||||
new Success(21),
|
||||
new Failure(new \Exception('When in the chronicle of wasted time')),
|
||||
]);
|
||||
};
|
||||
|
||||
$reactor = new NativeReactor;
|
||||
\Amp\resolve($gen(), $reactor)->wait();
|
||||
}
|
||||
|
||||
public function testImplicitCombinatorResolvesGeneratorInArray() {
|
||||
$gen1 = function() {
|
||||
yield 21;
|
||||
};
|
||||
|
||||
$gen2 = function() use ($gen1) {
|
||||
list($a, $b) = (yield [
|
||||
$gen1(),
|
||||
new Success(2)
|
||||
]);
|
||||
yield ($a * $b);
|
||||
};
|
||||
|
||||
$reactor = new NativeReactor;
|
||||
$result = \Amp\resolve($gen2(), $reactor)->wait();
|
||||
$this->assertSame(42, $result);
|
||||
}
|
||||
|
||||
public function testExplicitAllCombinatorResolution() {
|
||||
$gen = function() {
|
||||
list($a, $b, $c) = (yield 'all' => [
|
||||
new Success(21),
|
||||
new Success(2),
|
||||
10
|
||||
]);
|
||||
yield ($a * $b * $c);
|
||||
};
|
||||
|
||||
$reactor = new NativeReactor;
|
||||
$result = \Amp\resolve($gen(), $reactor)->wait();
|
||||
$this->assertSame(420, $result);
|
||||
}
|
||||
|
||||
public function testExplicitAnyCombinatorResolution() {
|
||||
$gen = function() {
|
||||
yield 'any' => [
|
||||
'a' => new Success(21),
|
||||
'b' => new Failure(new \Exception('test')),
|
||||
];
|
||||
};
|
||||
|
||||
$reactor = new NativeReactor;
|
||||
list($errors, $results) = \Amp\resolve($gen(), $reactor)->wait();
|
||||
|
||||
$this->assertSame('test', $errors['b']->getMessage());
|
||||
$this->assertSame(21, $results['a']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
* @expectedExceptionMessage All promises failed
|
||||
*/
|
||||
public function testExplicitSomeCombinatorResolutionFailsOnError() {
|
||||
$gen = function() {
|
||||
yield 'some' => [
|
||||
'r1' => new Failure(new \RuntimeException),
|
||||
'r2' => new Failure(new \RuntimeException),
|
||||
];
|
||||
};
|
||||
|
||||
$reactor = new NativeReactor;
|
||||
\Amp\resolve($gen(), $reactor)->wait();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \DomainException
|
||||
* @expectedExceptionMessage "some" yield command expects array; string yielded
|
||||
*/
|
||||
public function testExplicitCombinatorResolutionFailsIfNonArrayYielded() {
|
||||
$gen = function() {
|
||||
yield 'some' => 'test';
|
||||
};
|
||||
|
||||
$reactor = new NativeReactor;
|
||||
\Amp\resolve($gen(), $reactor)->wait();
|
||||
}
|
||||
|
||||
public function testExplicitImmediatelyYieldResolution() {
|
||||
$var = null;
|
||||
$gen = function() use (&$var) {
|
||||
yield 'immediately' => function() use (&$var) {
|
||||
$var = 42;
|
||||
};
|
||||
yield 'wait' => 100; // wait 100ms so the immediately callback executes
|
||||
};
|
||||
|
||||
$reactor = new NativeReactor;
|
||||
\Amp\resolve($gen(), $reactor)->wait();
|
||||
$this->assertSame(42, $var);
|
||||
}
|
||||
|
||||
public function testExplicitOnceYieldResolution() {
|
||||
$var = null;
|
||||
$gen = function() use (&$var) {
|
||||
yield 'once' => [function() use (&$var) { $var = 42; }, $msDelay=1];
|
||||
yield 'wait' => 100; // wait 100ms so the once callback executes
|
||||
};
|
||||
|
||||
$reactor = new NativeReactor;
|
||||
\Amp\resolve($gen(), $reactor)->wait();
|
||||
$this->assertSame(42, $var);
|
||||
}
|
||||
|
||||
public function testExplicitRepeatYieldResolution() {
|
||||
$var = 1;
|
||||
$repeatFunc = function($reactor, $watcherId) use (&$var) {
|
||||
yield 'cancel' => $watcherId;
|
||||
$var++;
|
||||
};
|
||||
|
||||
$gen = function() use (&$var, $repeatFunc) {
|
||||
yield 'repeat' => [$repeatFunc, $msDelay = 1];
|
||||
yield 'wait' => 100; // wait 100ms so we can be sure the repeat callback executes
|
||||
};
|
||||
|
||||
$reactor = new NativeReactor;
|
||||
\Amp\resolve($gen(), $reactor)->wait();
|
||||
$this->assertSame(2, $var);
|
||||
}
|
||||
}
|
@ -7,14 +7,14 @@ use Amp\NativeReactor;
|
||||
|
||||
class FutureTest extends \PHPUnit_Framework_TestCase {
|
||||
public function testPromiseReturnsSelf() {
|
||||
$future = new Future($this->getMock('Amp\Reactor'));
|
||||
$this->assertSame($future, $future->promise());
|
||||
$promisor = new Future;
|
||||
$this->assertSame($promisor, $promisor->promise());
|
||||
}
|
||||
|
||||
public function testWhenInvokesCallbackWithResultIfAlreadySucceeded() {
|
||||
$deferred = new Future($this->getMock('Amp\Reactor'));
|
||||
$promise = $deferred->promise();
|
||||
$deferred->succeed(42);
|
||||
$promisor = new Future;
|
||||
$promise = $promisor->promise();
|
||||
$promisor->succeed(42);
|
||||
$promise->when(function($e, $r) {
|
||||
$this->assertSame(42, $r);
|
||||
$this->assertNull($e);
|
||||
@ -22,7 +22,7 @@ class FutureTest extends \PHPUnit_Framework_TestCase {
|
||||
}
|
||||
|
||||
public function testWhenInvokesCallbackWithErrorIfAlreadyFailed() {
|
||||
$promisor = new Future($this->getMock('Amp\Reactor'));
|
||||
$promisor = new Future;
|
||||
$promise = $promisor->promise();
|
||||
$exception = new \Exception('test');
|
||||
$promisor->fail($exception);
|
||||
@ -32,19 +32,12 @@ class FutureTest extends \PHPUnit_Framework_TestCase {
|
||||
});
|
||||
}
|
||||
|
||||
public function testWaitReturnsOnResolution() {
|
||||
$reactor = new NativeReactor;
|
||||
$promisor = new Future($reactor);
|
||||
$reactor->once(function() use ($promisor) { $promisor->succeed(42); }, $msDelay = 100);
|
||||
$this->assertSame(42, $promisor->promise()->wait());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
* @expectedExceptionMessage Promise already resolved
|
||||
*/
|
||||
public function testSucceedThrowsIfAlreadyResolved() {
|
||||
$promisor = new Future($this->getMock('Amp\Reactor'));
|
||||
$promisor = new Future;
|
||||
$promisor->succeed(42);
|
||||
$promisor->succeed('zanzibar');
|
||||
}
|
||||
@ -54,7 +47,7 @@ class FutureTest extends \PHPUnit_Framework_TestCase {
|
||||
* @expectedExceptionMessage A Promise cannot act as its own resolution result
|
||||
*/
|
||||
public function testSucceedThrowsIfPromiseIsTheResolutionValue() {
|
||||
$promisor = new Future($this->getMock('Amp\Reactor'));
|
||||
$promisor = new Future;
|
||||
$promise = $promisor->promise();
|
||||
$promisor->succeed($promise);
|
||||
}
|
||||
@ -64,23 +57,20 @@ class FutureTest extends \PHPUnit_Framework_TestCase {
|
||||
* @expectedExceptionMessage Promise already resolved
|
||||
*/
|
||||
public function testFailThrowsIfAlreadyResolved() {
|
||||
$promisor = new Future($this->getMock('Amp\Reactor'));
|
||||
$promisor = new Future;
|
||||
$promisor->succeed(42);
|
||||
$promisor->fail(new \Exception);
|
||||
}
|
||||
|
||||
public function testSucceedingWithPromisePipelinesResult() {
|
||||
$reactor = new NativeReactor;
|
||||
$promisor = new Future($reactor);
|
||||
$next = new Future($reactor);
|
||||
|
||||
$reactor->once(function() use ($next) {
|
||||
$next->succeed(42);
|
||||
}, $msDelay = 1);
|
||||
|
||||
$promisor->succeed($next->promise());
|
||||
|
||||
$this->assertSame(42, $promisor->promise()->wait());
|
||||
(new NativeReactor)->run(function() {
|
||||
$next = new Future;
|
||||
$promisor = new Future;
|
||||
$promisor->succeed($next->promise());
|
||||
yield 'once' => [function() use ($next) { $next->succeed(42); }, $msDelay = 10];
|
||||
$result = (yield $promisor->promise());
|
||||
$this->assertSame(42, $result);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,15 +78,14 @@ class FutureTest extends \PHPUnit_Framework_TestCase {
|
||||
* @expectedExceptionMessage fugazi
|
||||
*/
|
||||
public function testFailingWithPromisePipelinesResult() {
|
||||
$reactor = new NativeReactor;
|
||||
$promisor = new Future($reactor);
|
||||
$next = new Future($reactor);
|
||||
(new NativeReactor)->run(function() {
|
||||
$promisor = new Future;
|
||||
$next = new Future;
|
||||
$once = function() use ($next) { $next->fail(new \RuntimeException('fugazi')); };
|
||||
yield 'once' => [$once, $msDelay = 10];
|
||||
$promisor->succeed($next->promise());
|
||||
|
||||
$reactor->once(function() use ($next) {
|
||||
$next->fail(new \RuntimeException('fugazi'));
|
||||
}, $msDelay = 10);
|
||||
|
||||
$promisor->succeed($next->promise());
|
||||
$promisor->promise()->wait();
|
||||
yield $promisor->promise();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
285
test/GeneratorResolverTest.php
Normal file
285
test/GeneratorResolverTest.php
Normal file
@ -0,0 +1,285 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Test;
|
||||
|
||||
use Amp\Success;
|
||||
use Amp\Failure;
|
||||
use Amp\NativeReactor;
|
||||
|
||||
class GeneratorResolverTest extends \PHPUnit_Framework_TestCase {
|
||||
public function testAllResolvesWithArrayOfResults() {
|
||||
(new NativeReactor)->run(function($reactor) {
|
||||
$expected = ['r1' => 42, 'r2' => 41];
|
||||
$actual = (yield 'all' => [
|
||||
'r1' => 42,
|
||||
'r2' => new Success(41),
|
||||
]);
|
||||
$this->assertSame($expected, $actual);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
* @expectedExceptionMessage zanzibar
|
||||
*/
|
||||
public function testAllThrowsIfAnyIndividualPromiseFails() {
|
||||
(new NativeReactor)->run(function($reactor) {
|
||||
$exception = new \RuntimeException('zanzibar');
|
||||
$promises = [
|
||||
'r1' => new Success(42),
|
||||
'r2' => new Failure($exception),
|
||||
'r3' => new Success(40),
|
||||
];
|
||||
$results = (yield $promises);
|
||||
});
|
||||
}
|
||||
|
||||
public function testSomeReturnsArrayOfErrorsAndResults() {
|
||||
(new NativeReactor)->run(function($reactor) {
|
||||
$exception = new \RuntimeException('zanzibar');
|
||||
$promises = [
|
||||
'r1' => new Success(42),
|
||||
'r2' => new Failure($exception),
|
||||
'r3' => new Success(40),
|
||||
];
|
||||
list($errors, $results) = (yield 'some' => $promises);
|
||||
$this->assertSame(['r2' => $exception], $errors);
|
||||
$this->assertSame(['r1' => 42, 'r3' => 40], $results);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
* @expectedExceptionMessage All promises failed
|
||||
*/
|
||||
public function testSomeThrowsIfNoPromisesResolveSuccessfully() {
|
||||
(new NativeReactor)->run(function($reactor) {
|
||||
$promises = [
|
||||
'r1' => new Failure(new \RuntimeException),
|
||||
'r2' => new Failure(new \RuntimeException),
|
||||
];
|
||||
list($errors, $results) = (yield 'some' => $promises);
|
||||
});
|
||||
}
|
||||
|
||||
public function testResolvedValueEqualsFinalYield() {
|
||||
(new NativeReactor)->run(function($reactor) {
|
||||
$gen = function() {
|
||||
$a = (yield 21);
|
||||
$b = (yield new Success(2));
|
||||
yield ($a * $b);
|
||||
};
|
||||
|
||||
$result = (yield $gen());
|
||||
$this->assertSame(42, $result);
|
||||
});
|
||||
}
|
||||
|
||||
public function testFutureErrorsAreThrownIntoGenerator() {
|
||||
(new NativeReactor)->run(function($reactor) {
|
||||
$gen = function() {
|
||||
$a = (yield 21);
|
||||
$b = 1;
|
||||
try {
|
||||
yield new Failure(new \Exception('test'));
|
||||
$this->fail('Code path should not be reached');
|
||||
} catch (\Exception $e) {
|
||||
$this->assertSame('test', $e->getMessage());
|
||||
$b = 2;
|
||||
}
|
||||
|
||||
yield ($a * $b);
|
||||
};
|
||||
|
||||
$result = (yield $gen());
|
||||
$this->assertSame(42, $result);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Exception
|
||||
* @expectedExceptionMessage When in the chronicle of wasted time
|
||||
*/
|
||||
public function testUncaughtGeneratorExceptionFailsResolverPromise() {
|
||||
(new NativeReactor)->run(function($reactor) {
|
||||
$gen = function() {
|
||||
yield;
|
||||
throw new \Exception('When in the chronicle of wasted time');
|
||||
yield;
|
||||
};
|
||||
|
||||
yield $gen();
|
||||
});
|
||||
}
|
||||
|
||||
public function testImplicitAllCombinatorResolution() {
|
||||
(new NativeReactor)->run(function($reactor) {
|
||||
$gen = function() {
|
||||
list($a, $b) = (yield [
|
||||
new Success(21),
|
||||
new Success(2),
|
||||
]);
|
||||
yield ($a * $b);
|
||||
};
|
||||
|
||||
$result = (yield $gen());
|
||||
$this->assertSame(42, $result);
|
||||
});
|
||||
}
|
||||
|
||||
public function testImplicitAllCombinatorResolutionWithNonPromises() {
|
||||
(new NativeReactor)->run(function($reactor) {
|
||||
$gen = function() {
|
||||
list($a, $b, $c) = (yield [new Success(21), new Success(2), 10]);
|
||||
yield ($a * $b * $c);
|
||||
};
|
||||
|
||||
$result = (yield $gen());
|
||||
$this->assertSame(420, $result);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Exception
|
||||
* @expectedExceptionMessage When in the chronicle of wasted time
|
||||
*/
|
||||
public function testImplicitAllCombinatorResolutionThrowsIfAnyOnePromiseFails() {
|
||||
$gen = function() {
|
||||
list($a, $b) = (yield [
|
||||
new Success(21),
|
||||
new Failure(new \Exception('When in the chronicle of wasted time')),
|
||||
]);
|
||||
};
|
||||
|
||||
$reactor = new NativeReactor;
|
||||
$reactor->run(function($reactor) use ($gen) {
|
||||
yield $gen();
|
||||
});
|
||||
}
|
||||
|
||||
public function testImplicitCombinatorResolvesGeneratorInArray() {
|
||||
(new NativeReactor)->run(function($reactor) {
|
||||
$gen1 = function() {
|
||||
yield 21;
|
||||
};
|
||||
|
||||
$gen2 = function() use ($gen1) {
|
||||
list($a, $b) = (yield [
|
||||
$gen1(),
|
||||
new Success(2)
|
||||
]);
|
||||
yield ($a * $b);
|
||||
};
|
||||
|
||||
|
||||
$result = (yield $gen2());
|
||||
$this->assertSame(42, $result);
|
||||
});
|
||||
}
|
||||
|
||||
public function testExplicitAllCombinatorResolution() {
|
||||
(new NativeReactor)->run(function($reactor) {
|
||||
$gen = function() {
|
||||
list($a, $b, $c) = (yield 'all' => [
|
||||
new Success(21),
|
||||
new Success(2),
|
||||
10
|
||||
]);
|
||||
yield ($a * $b * $c);
|
||||
};
|
||||
|
||||
$result = (yield $gen());
|
||||
$this->assertSame(420, $result);
|
||||
});
|
||||
}
|
||||
|
||||
public function testExplicitAnyCombinatorResolution() {
|
||||
(new NativeReactor)->run(function($reactor) {
|
||||
$gen = function() {
|
||||
yield 'any' => [
|
||||
'a' => new Success(21),
|
||||
'b' => new Failure(new \Exception('test')),
|
||||
];
|
||||
};
|
||||
|
||||
list($errors, $results) = (yield $gen());
|
||||
$this->assertSame('test', $errors['b']->getMessage());
|
||||
$this->assertSame(21, $results['a']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
* @expectedExceptionMessage All promises failed
|
||||
*/
|
||||
public function testExplicitSomeCombinatorResolutionFailsOnError() {
|
||||
(new NativeReactor)->run(function($reactor) {
|
||||
$gen = function() {
|
||||
yield 'some' => [
|
||||
'r1' => new Failure(new \RuntimeException),
|
||||
'r2' => new Failure(new \RuntimeException),
|
||||
];
|
||||
};
|
||||
yield $gen();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \DomainException
|
||||
* @expectedExceptionMessage "some" yield command expects array; string yielded
|
||||
*/
|
||||
public function testExplicitCombinatorResolutionFailsIfNonArrayYielded() {
|
||||
(new NativeReactor)->run(function($reactor) {
|
||||
$gen = function() {
|
||||
yield 'some' => 'test';
|
||||
};
|
||||
yield $gen();
|
||||
});
|
||||
}
|
||||
|
||||
public function testExplicitImmediatelyYieldResolution() {
|
||||
(new NativeReactor)->run(function($reactor) {
|
||||
$gen = function() {
|
||||
$var = null;
|
||||
yield 'immediately' => function() use (&$var) { $var = 42; };
|
||||
yield 'wait' => 100; // wait 100ms so the immediately callback executes
|
||||
yield $var;
|
||||
};
|
||||
$result = (yield $gen());
|
||||
$this->assertSame(42, $result);
|
||||
});
|
||||
}
|
||||
|
||||
public function testExplicitOnceYieldResolution() {
|
||||
(new NativeReactor)->run(function($reactor) {
|
||||
$gen = function() {
|
||||
$var = null;
|
||||
yield 'once' => [function() use (&$var) { $var = 42; }, $msDelay = 1];
|
||||
yield 'wait' => 100; // wait 100ms so the once callback executes
|
||||
yield $var;
|
||||
};
|
||||
$result = (yield $gen());
|
||||
$this->assertSame(42, $result);
|
||||
});
|
||||
}
|
||||
|
||||
public function testExplicitRepeatYieldResolution() {
|
||||
(new NativeReactor)->run(function($reactor) {
|
||||
$var = null;
|
||||
$repeatFunc = function($reactor, $watcherId) use (&$var) {
|
||||
$var = 1;
|
||||
yield 'cancel' => $watcherId;
|
||||
$var++;
|
||||
};
|
||||
|
||||
$gen = function() use (&$var, $repeatFunc) {
|
||||
yield 'repeat' => [$repeatFunc, $msDelay = 1];
|
||||
yield 'wait' => 100; // wait 100ms so we can be sure the repeat callback executes
|
||||
yield $var;
|
||||
};
|
||||
|
||||
$result = (yield $gen());
|
||||
$this->assertSame(2, $result);
|
||||
});
|
||||
}
|
||||
}
|
@ -7,8 +7,8 @@ use Amp\NativeReactor;
|
||||
|
||||
class PrivateFutureTest extends \PHPUnit_Framework_TestCase {
|
||||
public function testPromiseReturnsUnresolvedInstance() {
|
||||
$future = new PrivateFuture($this->getMock('Amp\Reactor'));
|
||||
$this->assertInstanceOf('Amp\Unresolved', $future->promise());
|
||||
$promisor = new PrivateFuture;
|
||||
$this->assertInstanceOf('Amp\Unresolved', $promisor->promise());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -16,7 +16,7 @@ class PrivateFutureTest extends \PHPUnit_Framework_TestCase {
|
||||
* @expectedExceptionMessage Promise already resolved
|
||||
*/
|
||||
public function testSucceedThrowsIfAlreadyResolved() {
|
||||
$promisor = new PrivateFuture($this->getMock('Amp\Reactor'));
|
||||
$promisor = new PrivateFuture;
|
||||
$promisor->succeed(42);
|
||||
$promisor->succeed('zanzibar');
|
||||
}
|
||||
@ -26,7 +26,7 @@ class PrivateFutureTest extends \PHPUnit_Framework_TestCase {
|
||||
* @expectedExceptionMessage A Promise cannot act as its own resolution result
|
||||
*/
|
||||
public function testSucceedThrowsIfPromiseIsTheResolutionValue() {
|
||||
$promisor = new PrivateFuture($this->getMock('Amp\Reactor'));
|
||||
$promisor = new PrivateFuture;
|
||||
$promise = $promisor->promise();
|
||||
$promisor->succeed($promise);
|
||||
}
|
||||
@ -36,23 +36,22 @@ class PrivateFutureTest extends \PHPUnit_Framework_TestCase {
|
||||
* @expectedExceptionMessage Promise already resolved
|
||||
*/
|
||||
public function testFailThrowsIfAlreadyResolved() {
|
||||
$promisor = new PrivateFuture($this->getMock('Amp\Reactor'));
|
||||
$promisor = new PrivateFuture;
|
||||
$promisor->succeed(42);
|
||||
$promisor->fail(new \Exception);
|
||||
}
|
||||
|
||||
public function testSucceedingWithPromisePipelinesResult() {
|
||||
$reactor = new NativeReactor;
|
||||
$promisor = new PrivateFuture($reactor);
|
||||
$next = new PrivateFuture($reactor);
|
||||
|
||||
$reactor->once(function() use ($next) {
|
||||
$next->succeed(42);
|
||||
}, $msDelay = 1);
|
||||
|
||||
$promisor->succeed($next->promise());
|
||||
|
||||
$this->assertSame(42, $promisor->promise()->wait());
|
||||
(new NativeReactor)->run(function($reactor) {
|
||||
$promisor = new PrivateFuture;
|
||||
$next = new PrivateFuture;
|
||||
$reactor->once(function() use ($next) {
|
||||
$next->succeed(42);
|
||||
}, $msDelay = 1);
|
||||
$promisor->succeed($next->promise());
|
||||
$result = (yield $promisor->promise());
|
||||
$this->assertSame(42, $result);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -60,15 +59,17 @@ class PrivateFutureTest extends \PHPUnit_Framework_TestCase {
|
||||
* @expectedExceptionMessage fugazi
|
||||
*/
|
||||
public function testFailingWithPromisePipelinesResult() {
|
||||
$reactor = new NativeReactor;
|
||||
$promisor = new PrivateFuture($reactor);
|
||||
$next = new PrivateFuture($reactor);
|
||||
(new NativeReactor)->run(function($reactor) {
|
||||
$promisor = new PrivateFuture;
|
||||
$next = new PrivateFuture;
|
||||
|
||||
$reactor->once(function() use ($next) {
|
||||
$next->fail(new \RuntimeException('fugazi'));
|
||||
}, $msDelay = 10);
|
||||
$reactor->once(function() use ($next) {
|
||||
$next->fail(new \RuntimeException('fugazi'));
|
||||
}, $msDelay = 10);
|
||||
|
||||
$promisor->succeed($next->promise());
|
||||
$promisor->promise()->wait();
|
||||
$promisor->succeed($next->promise());
|
||||
|
||||
yield $promisor->promise();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -321,4 +321,12 @@ abstract class ReactorTest extends \PHPUnit_Framework_TestCase {
|
||||
$reactor->run();
|
||||
$this->assertSame("Art is the supreme task", $test);
|
||||
}
|
||||
|
||||
public function testOnErrorCallbackInterceptsUncaughtException() {
|
||||
$var = null;
|
||||
$reactor = $this->getReactor();
|
||||
$reactor->onError(function($e) use (&$var) { $var = $e->getMessage(); });
|
||||
$reactor->run(function() { throw new \Exception('test'); });
|
||||
$this->assertSame('test', $var);
|
||||
}
|
||||
}
|
||||
|
@ -14,10 +14,4 @@ class SuccessTest extends \PHPUnit_Framework_TestCase {
|
||||
$this->assertSame($value, $result);
|
||||
});
|
||||
}
|
||||
|
||||
public function testWaitReturnsResolvedValue() {
|
||||
$value = 42;
|
||||
$success = new Success($value);
|
||||
$this->assertSame($value, $success->wait());
|
||||
}
|
||||
}
|
||||
|
@ -8,9 +8,9 @@ use Amp\NativeReactor;
|
||||
|
||||
class UnresolvedTest extends \PHPUnit_Framework_TestCase {
|
||||
public function testWatchInvokesCallbackWithResultIfAlreadySucceeded() {
|
||||
$deferred = new PrivateFuture($this->getMock('Amp\Reactor'));
|
||||
$promise = $deferred->promise();
|
||||
$deferred->succeed(42);
|
||||
$promisor = new PrivateFuture;
|
||||
$promise = $promisor->promise();
|
||||
$promisor->succeed(42);
|
||||
$promise->watch(function($p, $e, $r) {
|
||||
$this->assertSame(42, $r);
|
||||
$this->assertNull($p);
|
||||
@ -19,7 +19,7 @@ class UnresolvedTest extends \PHPUnit_Framework_TestCase {
|
||||
}
|
||||
|
||||
public function testWatchInvokesCallbackWithErrorIfAlreadyFailed() {
|
||||
$promisor = new PrivateFuture($this->getMock('Amp\Reactor'));
|
||||
$promisor = new PrivateFuture;
|
||||
$promise = $promisor->promise();
|
||||
$exception = new \Exception('test');
|
||||
$promisor->fail($exception);
|
||||
@ -30,19 +30,12 @@ class UnresolvedTest extends \PHPUnit_Framework_TestCase {
|
||||
});
|
||||
}
|
||||
|
||||
public function testWaitReturnsOnResolution() {
|
||||
$reactor = new NativeReactor;
|
||||
$promisor = new PrivateFuture($reactor);
|
||||
$reactor->once(function() use ($promisor) { $promisor->succeed(42); }, $msDelay = 100);
|
||||
$this->assertSame(42, $promisor->promise()->wait());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
* @expectedExceptionMessage Promise already resolved
|
||||
*/
|
||||
public function testSucceedThrowsIfAlreadyResolved() {
|
||||
$promisor = new PrivateFuture($this->getMock('Amp\Reactor'));
|
||||
$promisor = new PrivateFuture;
|
||||
$promisor->succeed(42);
|
||||
$promisor->succeed('zanzibar');
|
||||
}
|
||||
@ -52,7 +45,7 @@ class UnresolvedTest extends \PHPUnit_Framework_TestCase {
|
||||
* @expectedExceptionMessage A Promise cannot act as its own resolution result
|
||||
*/
|
||||
public function testSucceedThrowsIfPromiseIsTheResolutionValue() {
|
||||
$promisor = new PrivateFuture($this->getMock('Amp\Reactor'));
|
||||
$promisor = new PrivateFuture;
|
||||
$promise = $promisor->promise();
|
||||
$promisor->succeed($promise);
|
||||
}
|
||||
@ -62,23 +55,24 @@ class UnresolvedTest extends \PHPUnit_Framework_TestCase {
|
||||
* @expectedExceptionMessage Promise already resolved
|
||||
*/
|
||||
public function testFailThrowsIfAlreadyResolved() {
|
||||
$promisor = new PrivateFuture($this->getMock('Amp\Reactor'));
|
||||
$promisor = new PrivateFuture;
|
||||
$promisor->succeed(42);
|
||||
$promisor->fail(new \Exception);
|
||||
}
|
||||
|
||||
public function testSucceedingWithPromisePipelinesResult() {
|
||||
$reactor = new NativeReactor;
|
||||
$promisor = new PrivateFuture($reactor);
|
||||
$next = new Future($reactor);
|
||||
(new NativeReactor)->run(function($reactor) {
|
||||
$promisor = new PrivateFuture;
|
||||
$next = new Future;
|
||||
|
||||
$reactor->once(function() use ($next) {
|
||||
$next->succeed(42);
|
||||
}, $msDelay = 1);
|
||||
$reactor->once(function() use ($next) {
|
||||
$next->succeed(42);
|
||||
}, $msDelay = 1);
|
||||
|
||||
$promisor->succeed($next->promise());
|
||||
$promisor->succeed($next->promise());
|
||||
|
||||
$this->assertSame(42, $promisor->promise()->wait());
|
||||
$this->assertSame(42, (yield $promisor->promise()));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -86,15 +80,16 @@ class UnresolvedTest extends \PHPUnit_Framework_TestCase {
|
||||
* @expectedExceptionMessage fugazi
|
||||
*/
|
||||
public function testFailingWithPromisePipelinesResult() {
|
||||
$reactor = new NativeReactor;
|
||||
$promisor = new PrivateFuture($reactor);
|
||||
$next = new Future($reactor);
|
||||
(new NativeReactor)->run(function($reactor) {
|
||||
$promisor = new PrivateFuture;
|
||||
$next = new Future;
|
||||
|
||||
$reactor->once(function() use ($next) {
|
||||
$next->fail(new \RuntimeException('fugazi'));
|
||||
}, $msDelay = 10);
|
||||
$reactor->once(function() use ($next) {
|
||||
$next->fail(new \RuntimeException('fugazi'));
|
||||
}, $msDelay = 10);
|
||||
|
||||
$promisor->succeed($next->promise());
|
||||
$promisor->promise()->wait();
|
||||
$promisor->succeed($next->promise());
|
||||
yield $promisor->promise();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user