Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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