summaryrefslogtreecommitdiff
path: root/vendor/guzzlehttp/promises/src/Coroutine.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/guzzlehttp/promises/src/Coroutine.php')
-rw-r--r--vendor/guzzlehttp/promises/src/Coroutine.php162
1 files changed, 162 insertions, 0 deletions
diff --git a/vendor/guzzlehttp/promises/src/Coroutine.php b/vendor/guzzlehttp/promises/src/Coroutine.php
new file mode 100644
index 000000000..0b5b9c0a4
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/Coroutine.php
@@ -0,0 +1,162 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Promise;
+
+use Generator;
+use Throwable;
+
+/**
+ * Creates a promise that is resolved using a generator that yields values or
+ * promises (somewhat similar to C#'s async keyword).
+ *
+ * When called, the Coroutine::of method will start an instance of the generator
+ * and returns a promise that is fulfilled with its final yielded value.
+ *
+ * Control is returned back to the generator when the yielded promise settles.
+ * This can lead to less verbose code when doing lots of sequential async calls
+ * with minimal processing in between.
+ *
+ * use GuzzleHttp\Promise;
+ *
+ * function createPromise($value) {
+ * return new Promise\FulfilledPromise($value);
+ * }
+ *
+ * $promise = Promise\Coroutine::of(function () {
+ * $value = (yield createPromise('a'));
+ * try {
+ * $value = (yield createPromise($value . 'b'));
+ * } catch (\Throwable $e) {
+ * // The promise was rejected.
+ * }
+ * yield $value . 'c';
+ * });
+ *
+ * // Outputs "abc"
+ * $promise->then(function ($v) { echo $v; });
+ *
+ * @param callable $generatorFn Generator function to wrap into a promise.
+ *
+ * @return Promise
+ *
+ * @see https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration
+ */
+final class Coroutine implements PromiseInterface
+{
+ /**
+ * @var PromiseInterface|null
+ */
+ private $currentPromise;
+
+ /**
+ * @var Generator
+ */
+ private $generator;
+
+ /**
+ * @var Promise
+ */
+ private $result;
+
+ public function __construct(callable $generatorFn)
+ {
+ $this->generator = $generatorFn();
+ $this->result = new Promise(function (): void {
+ while (isset($this->currentPromise)) {
+ $this->currentPromise->wait();
+ }
+ });
+ try {
+ $this->nextCoroutine($this->generator->current());
+ } catch (Throwable $throwable) {
+ $this->result->reject($throwable);
+ }
+ }
+
+ /**
+ * Create a new coroutine.
+ */
+ public static function of(callable $generatorFn): self
+ {
+ return new self($generatorFn);
+ }
+
+ public function then(
+ callable $onFulfilled = null,
+ callable $onRejected = null
+ ): PromiseInterface {
+ return $this->result->then($onFulfilled, $onRejected);
+ }
+
+ public function otherwise(callable $onRejected): PromiseInterface
+ {
+ return $this->result->otherwise($onRejected);
+ }
+
+ public function wait(bool $unwrap = true)
+ {
+ return $this->result->wait($unwrap);
+ }
+
+ public function getState(): string
+ {
+ return $this->result->getState();
+ }
+
+ public function resolve($value): void
+ {
+ $this->result->resolve($value);
+ }
+
+ public function reject($reason): void
+ {
+ $this->result->reject($reason);
+ }
+
+ public function cancel(): void
+ {
+ $this->currentPromise->cancel();
+ $this->result->cancel();
+ }
+
+ private function nextCoroutine($yielded): void
+ {
+ $this->currentPromise = Create::promiseFor($yielded)
+ ->then([$this, '_handleSuccess'], [$this, '_handleFailure']);
+ }
+
+ /**
+ * @internal
+ */
+ public function _handleSuccess($value): void
+ {
+ unset($this->currentPromise);
+ try {
+ $next = $this->generator->send($value);
+ if ($this->generator->valid()) {
+ $this->nextCoroutine($next);
+ } else {
+ $this->result->resolve($value);
+ }
+ } catch (Throwable $throwable) {
+ $this->result->reject($throwable);
+ }
+ }
+
+ /**
+ * @internal
+ */
+ public function _handleFailure($reason): void
+ {
+ unset($this->currentPromise);
+ try {
+ $nextYield = $this->generator->throw(Create::exceptionFor($reason));
+ // The throw was caught, so keep iterating on the coroutine
+ $this->nextCoroutine($nextYield);
+ } catch (Throwable $throwable) {
+ $this->result->reject($throwable);
+ }
+ }
+}