Chris@0: then(function ($v) { echo $v; }); Chris@0: * Chris@0: * @param callable $generatorFn Generator function to wrap into a promise. Chris@0: * Chris@0: * @return Promise Chris@0: * @link https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration Chris@0: */ Chris@0: final class Coroutine implements PromiseInterface Chris@0: { Chris@0: /** Chris@0: * @var PromiseInterface|null Chris@0: */ Chris@0: private $currentPromise; Chris@0: Chris@0: /** Chris@0: * @var Generator Chris@0: */ Chris@0: private $generator; Chris@0: Chris@0: /** Chris@0: * @var Promise Chris@0: */ Chris@0: private $result; Chris@0: Chris@0: public function __construct(callable $generatorFn) Chris@0: { Chris@0: $this->generator = $generatorFn(); Chris@0: $this->result = new Promise(function () { Chris@0: while (isset($this->currentPromise)) { Chris@0: $this->currentPromise->wait(); Chris@0: } Chris@0: }); Chris@0: $this->nextCoroutine($this->generator->current()); Chris@0: } Chris@0: Chris@0: public function then( Chris@0: callable $onFulfilled = null, Chris@0: callable $onRejected = null Chris@0: ) { Chris@0: return $this->result->then($onFulfilled, $onRejected); Chris@0: } Chris@0: Chris@0: public function otherwise(callable $onRejected) Chris@0: { Chris@0: return $this->result->otherwise($onRejected); Chris@0: } Chris@0: Chris@0: public function wait($unwrap = true) Chris@0: { Chris@0: return $this->result->wait($unwrap); Chris@0: } Chris@0: Chris@0: public function getState() Chris@0: { Chris@0: return $this->result->getState(); Chris@0: } Chris@0: Chris@0: public function resolve($value) Chris@0: { Chris@0: $this->result->resolve($value); Chris@0: } Chris@0: Chris@0: public function reject($reason) Chris@0: { Chris@0: $this->result->reject($reason); Chris@0: } Chris@0: Chris@0: public function cancel() Chris@0: { Chris@0: $this->currentPromise->cancel(); Chris@0: $this->result->cancel(); Chris@0: } Chris@0: Chris@0: private function nextCoroutine($yielded) Chris@0: { Chris@0: $this->currentPromise = promise_for($yielded) Chris@0: ->then([$this, '_handleSuccess'], [$this, '_handleFailure']); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @internal Chris@0: */ Chris@0: public function _handleSuccess($value) Chris@0: { Chris@0: unset($this->currentPromise); Chris@0: try { Chris@0: $next = $this->generator->send($value); Chris@0: if ($this->generator->valid()) { Chris@0: $this->nextCoroutine($next); Chris@0: } else { Chris@0: $this->result->resolve($value); Chris@0: } Chris@0: } catch (Exception $exception) { Chris@0: $this->result->reject($exception); Chris@0: } catch (Throwable $throwable) { Chris@0: $this->result->reject($throwable); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * @internal Chris@0: */ Chris@0: public function _handleFailure($reason) Chris@0: { Chris@0: unset($this->currentPromise); Chris@0: try { Chris@0: $nextYield = $this->generator->throw(exception_for($reason)); Chris@0: // The throw was caught, so keep iterating on the coroutine Chris@0: $this->nextCoroutine($nextYield); Chris@0: } catch (Exception $exception) { Chris@0: $this->result->reject($exception); Chris@0: } catch (Throwable $throwable) { Chris@0: $this->result->reject($throwable); Chris@0: } Chris@0: } Chris@0: }