annotate vendor/guzzlehttp/promises/src/Coroutine.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 4c8ae668cc8c
children
rev   line source
Chris@0 1 <?php
Chris@0 2 namespace GuzzleHttp\Promise;
Chris@0 3
Chris@0 4 use Exception;
Chris@0 5 use Generator;
Chris@0 6 use Throwable;
Chris@0 7
Chris@0 8 /**
Chris@0 9 * Creates a promise that is resolved using a generator that yields values or
Chris@0 10 * promises (somewhat similar to C#'s async keyword).
Chris@0 11 *
Chris@0 12 * When called, the coroutine function will start an instance of the generator
Chris@0 13 * and returns a promise that is fulfilled with its final yielded value.
Chris@0 14 *
Chris@0 15 * Control is returned back to the generator when the yielded promise settles.
Chris@0 16 * This can lead to less verbose code when doing lots of sequential async calls
Chris@0 17 * with minimal processing in between.
Chris@0 18 *
Chris@0 19 * use GuzzleHttp\Promise;
Chris@0 20 *
Chris@0 21 * function createPromise($value) {
Chris@0 22 * return new Promise\FulfilledPromise($value);
Chris@0 23 * }
Chris@0 24 *
Chris@0 25 * $promise = Promise\coroutine(function () {
Chris@0 26 * $value = (yield createPromise('a'));
Chris@0 27 * try {
Chris@0 28 * $value = (yield createPromise($value . 'b'));
Chris@0 29 * } catch (\Exception $e) {
Chris@0 30 * // The promise was rejected.
Chris@0 31 * }
Chris@0 32 * yield $value . 'c';
Chris@0 33 * });
Chris@0 34 *
Chris@0 35 * // Outputs "abc"
Chris@0 36 * $promise->then(function ($v) { echo $v; });
Chris@0 37 *
Chris@0 38 * @param callable $generatorFn Generator function to wrap into a promise.
Chris@0 39 *
Chris@0 40 * @return Promise
Chris@0 41 * @link https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration
Chris@0 42 */
Chris@0 43 final class Coroutine implements PromiseInterface
Chris@0 44 {
Chris@0 45 /**
Chris@0 46 * @var PromiseInterface|null
Chris@0 47 */
Chris@0 48 private $currentPromise;
Chris@0 49
Chris@0 50 /**
Chris@0 51 * @var Generator
Chris@0 52 */
Chris@0 53 private $generator;
Chris@0 54
Chris@0 55 /**
Chris@0 56 * @var Promise
Chris@0 57 */
Chris@0 58 private $result;
Chris@0 59
Chris@0 60 public function __construct(callable $generatorFn)
Chris@0 61 {
Chris@0 62 $this->generator = $generatorFn();
Chris@0 63 $this->result = new Promise(function () {
Chris@0 64 while (isset($this->currentPromise)) {
Chris@0 65 $this->currentPromise->wait();
Chris@0 66 }
Chris@0 67 });
Chris@0 68 $this->nextCoroutine($this->generator->current());
Chris@0 69 }
Chris@0 70
Chris@0 71 public function then(
Chris@0 72 callable $onFulfilled = null,
Chris@0 73 callable $onRejected = null
Chris@0 74 ) {
Chris@0 75 return $this->result->then($onFulfilled, $onRejected);
Chris@0 76 }
Chris@0 77
Chris@0 78 public function otherwise(callable $onRejected)
Chris@0 79 {
Chris@0 80 return $this->result->otherwise($onRejected);
Chris@0 81 }
Chris@0 82
Chris@0 83 public function wait($unwrap = true)
Chris@0 84 {
Chris@0 85 return $this->result->wait($unwrap);
Chris@0 86 }
Chris@0 87
Chris@0 88 public function getState()
Chris@0 89 {
Chris@0 90 return $this->result->getState();
Chris@0 91 }
Chris@0 92
Chris@0 93 public function resolve($value)
Chris@0 94 {
Chris@0 95 $this->result->resolve($value);
Chris@0 96 }
Chris@0 97
Chris@0 98 public function reject($reason)
Chris@0 99 {
Chris@0 100 $this->result->reject($reason);
Chris@0 101 }
Chris@0 102
Chris@0 103 public function cancel()
Chris@0 104 {
Chris@0 105 $this->currentPromise->cancel();
Chris@0 106 $this->result->cancel();
Chris@0 107 }
Chris@0 108
Chris@0 109 private function nextCoroutine($yielded)
Chris@0 110 {
Chris@0 111 $this->currentPromise = promise_for($yielded)
Chris@0 112 ->then([$this, '_handleSuccess'], [$this, '_handleFailure']);
Chris@0 113 }
Chris@0 114
Chris@0 115 /**
Chris@0 116 * @internal
Chris@0 117 */
Chris@0 118 public function _handleSuccess($value)
Chris@0 119 {
Chris@0 120 unset($this->currentPromise);
Chris@0 121 try {
Chris@0 122 $next = $this->generator->send($value);
Chris@0 123 if ($this->generator->valid()) {
Chris@0 124 $this->nextCoroutine($next);
Chris@0 125 } else {
Chris@0 126 $this->result->resolve($value);
Chris@0 127 }
Chris@0 128 } catch (Exception $exception) {
Chris@0 129 $this->result->reject($exception);
Chris@0 130 } catch (Throwable $throwable) {
Chris@0 131 $this->result->reject($throwable);
Chris@0 132 }
Chris@0 133 }
Chris@0 134
Chris@0 135 /**
Chris@0 136 * @internal
Chris@0 137 */
Chris@0 138 public function _handleFailure($reason)
Chris@0 139 {
Chris@0 140 unset($this->currentPromise);
Chris@0 141 try {
Chris@0 142 $nextYield = $this->generator->throw(exception_for($reason));
Chris@0 143 // The throw was caught, so keep iterating on the coroutine
Chris@0 144 $this->nextCoroutine($nextYield);
Chris@0 145 } catch (Exception $exception) {
Chris@0 146 $this->result->reject($exception);
Chris@0 147 } catch (Throwable $throwable) {
Chris@0 148 $this->result->reject($throwable);
Chris@0 149 }
Chris@0 150 }
Chris@0 151 }