PluginProbe
Plausible Analytics / trunk
Plausible Analytics vtrunk
2.6.1 2.6.0 trunk 1.0.0 1.0.1 1.1.0 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 All 52 releases
plausible-analytics / src / Client / lib / Lib / GuzzleHttp / Promise / Coroutine.php

Coroutine.php in Plausible Analytics trunk, at src/Client/lib/Lib/GuzzleHttp/Promise/Coroutine.php

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