2 namespace GuzzleHttp\Promise;
9 * Creates a promise that is resolved using a generator that yields values or
10 * promises (somewhat similar to C#'s async keyword).
12 * When called, the coroutine function will start an instance of the generator
13 * and returns a promise that is fulfilled with its final yielded value.
15 * Control is returned back to the generator when the yielded promise settles.
16 * This can lead to less verbose code when doing lots of sequential async calls
17 * with minimal processing in between.
19 * use GuzzleHttp\Promise;
21 * function createPromise($value) {
22 * return new Promise\FulfilledPromise($value);
25 * $promise = Promise\coroutine(function () {
26 * $value = (yield createPromise('a'));
28 * $value = (yield createPromise($value . 'b'));
29 * } catch (\Exception $e) {
30 * // The promise was rejected.
36 * $promise->then(function ($v) { echo $v; });
38 * @param callable $generatorFn Generator function to wrap into a promise.
41 * @link https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration
43 final class Coroutine implements PromiseInterface
46 * @var PromiseInterface|null
48 private $currentPromise;
60 public function __construct(callable $generatorFn)
62 $this->generator = $generatorFn();
63 $this->result = new Promise(function () {
64 while (isset($this->currentPromise)) {
65 $this->currentPromise->wait();
68 $this->nextCoroutine($this->generator->current());
72 callable $onFulfilled = null,
73 callable $onRejected = null
75 return $this->result->then($onFulfilled, $onRejected);
78 public function otherwise(callable $onRejected)
80 return $this->result->otherwise($onRejected);
83 public function wait($unwrap = true)
85 return $this->result->wait($unwrap);
88 public function getState()
90 return $this->result->getState();
93 public function resolve($value)
95 $this->result->resolve($value);
98 public function reject($reason)
100 $this->result->reject($reason);
103 public function cancel()
105 $this->currentPromise->cancel();
106 $this->result->cancel();
109 private function nextCoroutine($yielded)
111 $this->currentPromise = promise_for($yielded)
112 ->then([$this, '_handleSuccess'], [$this, '_handleFailure']);
118 public function _handleSuccess($value)
120 unset($this->currentPromise);
122 $next = $this->generator->send($value);
123 if ($this->generator->valid()) {
124 $this->nextCoroutine($next);
126 $this->result->resolve($value);
128 } catch (Exception $exception) {
129 $this->result->reject($exception);
130 } catch (Throwable $throwable) {
131 $this->result->reject($throwable);
138 public function _handleFailure($reason)
140 unset($this->currentPromise);
142 $nextYield = $this->generator->throw(exception_for($reason));
143 // The throw was caught, so keep iterating on the coroutine
144 $this->nextCoroutine($nextYield);
145 } catch (Exception $exception) {
146 $this->result->reject($exception);
147 } catch (Throwable $throwable) {
148 $this->result->reject($throwable);