Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
 
1441 ariadna 3
declare(strict_types=1);
4
 
1 efrain 5
namespace 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 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'));
1441 ariadna 31
 *         } catch (\Throwable $e) {
1 efrain 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
 *
1441 ariadna 44
 * @see https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration
1 efrain 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();
1441 ariadna 66
        $this->result = new Promise(function (): void {
1 efrain 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
     */
1441 ariadna 81
    public static function of(callable $generatorFn): self
1 efrain 82
    {
83
        return new self($generatorFn);
84
    }
85
 
86
    public function then(
1441 ariadna 87
        ?callable $onFulfilled = null,
88
        ?callable $onRejected = null
89
    ): PromiseInterface {
1 efrain 90
        return $this->result->then($onFulfilled, $onRejected);
91
    }
92
 
1441 ariadna 93
    public function otherwise(callable $onRejected): PromiseInterface
1 efrain 94
    {
95
        return $this->result->otherwise($onRejected);
96
    }
97
 
1441 ariadna 98
    public function wait(bool $unwrap = true)
1 efrain 99
    {
100
        return $this->result->wait($unwrap);
101
    }
102
 
1441 ariadna 103
    public function getState(): string
1 efrain 104
    {
105
        return $this->result->getState();
106
    }
107
 
1441 ariadna 108
    public function resolve($value): void
1 efrain 109
    {
110
        $this->result->resolve($value);
111
    }
112
 
1441 ariadna 113
    public function reject($reason): void
1 efrain 114
    {
115
        $this->result->reject($reason);
116
    }
117
 
1441 ariadna 118
    public function cancel(): void
1 efrain 119
    {
120
        $this->currentPromise->cancel();
121
        $this->result->cancel();
122
    }
123
 
1441 ariadna 124
    private function nextCoroutine($yielded): void
1 efrain 125
    {
126
        $this->currentPromise = Create::promiseFor($yielded)
127
            ->then([$this, '_handleSuccess'], [$this, '_handleFailure']);
128
    }
129
 
130
    /**
131
     * @internal
132
     */
1441 ariadna 133
    public function _handleSuccess($value): void
1 efrain 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
     */
1441 ariadna 151
    public function _handleFailure($reason): void
1 efrain 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
}