krz/vox-populi

A Tumblr web client.

clone: git clone https://gitbay.org/krz/vox-populi.git

main: vendor/guzzlehttp/promises/src/Coroutine.php · raw

  1<?php
  2namespace GuzzleHttp\Promise;
  3
  4use Exception;
  5use Generator;
  6use Throwable;
  7
  8/**
  9 * Creates a promise that is resolved using a generator that yields values or
 10 * promises (somewhat similar to C#'s async keyword).
 11 *
 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.
 14 *
 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.
 18 *
 19 *     use GuzzleHttp\Promise;
 20 *
 21 *     function createPromise($value) {
 22 *         return new Promise\FulfilledPromise($value);
 23 *     }
 24 *
 25 *     $promise = Promise\coroutine(function () {
 26 *         $value = (yield createPromise('a'));
 27 *         try {
 28 *             $value = (yield createPromise($value . 'b'));
 29 *         } catch (\Exception $e) {
 30 *             // The promise was rejected.
 31 *         }
 32 *         yield $value . 'c';
 33 *     });
 34 *
 35 *     // Outputs "abc"
 36 *     $promise->then(function ($v) { echo $v; });
 37 *
 38 * @param callable $generatorFn Generator function to wrap into a promise.
 39 *
 40 * @return Promise
 41 * @link https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration
 42 */
 43final class Coroutine implements PromiseInterface
 44{
 45    /**
 46     * @var PromiseInterface|null
 47     */
 48    private $currentPromise;
 49
 50    /**
 51     * @var Generator
 52     */
 53    private $generator;
 54
 55    /**
 56     * @var Promise
 57     */
 58    private $result;
 59
 60    public function __construct(callable $generatorFn)
 61    {
 62        $this->generator = $generatorFn();
 63        $this->result = new Promise(function () {
 64            while (isset($this->currentPromise)) {
 65                $this->currentPromise->wait();
 66            }
 67        });
 68        $this->nextCoroutine($this->generator->current());
 69    }
 70
 71    public function then(
 72        callable $onFulfilled = null,
 73        callable $onRejected = null
 74    ) {
 75        return $this->result->then($onFulfilled, $onRejected);
 76    }
 77
 78    public function otherwise(callable $onRejected)
 79    {
 80        return $this->result->otherwise($onRejected);
 81    }
 82
 83    public function wait($unwrap = true)
 84    {
 85        return $this->result->wait($unwrap);
 86    }
 87
 88    public function getState()
 89    {
 90        return $this->result->getState();
 91    }
 92
 93    public function resolve($value)
 94    {
 95        $this->result->resolve($value);
 96    }
 97
 98    public function reject($reason)
 99    {
100        $this->result->reject($reason);
101    }
102
103    public function cancel()
104    {
105        $this->currentPromise->cancel();
106        $this->result->cancel();
107    }
108
109    private function nextCoroutine($yielded)
110    {
111        $this->currentPromise = promise_for($yielded)
112            ->then([$this, '_handleSuccess'], [$this, '_handleFailure']);
113    }
114
115    /**
116     * @internal
117     */
118    public function _handleSuccess($value)
119    {
120        unset($this->currentPromise);
121        try {
122            $next = $this->generator->send($value);
123            if ($this->generator->valid()) {
124                $this->nextCoroutine($next);
125            } else {
126                $this->result->resolve($value);
127            }
128        } catch (Exception $exception) {
129            $this->result->reject($exception);
130        } catch (Throwable $throwable) {
131            $this->result->reject($throwable);
132        }
133    }
134
135    /**
136     * @internal
137     */
138    public function _handleFailure($reason)
139    {
140        unset($this->currentPromise);
141        try {
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);
149        }
150    }
151}