Verzeichnisstruktur phpBB-3.3.15
- Veröffentlicht
- 28.08.2024
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
README.md
001 # Guzzle Promises
002
003 [Promises/A+](https://promisesaplus.com/) implementation that handles promise
004 chaining and resolution iteratively, allowing for "infinite" promise chaining
005 while keeping the stack size constant. Read [this blog post](https://blog.domenic.me/youre-missing-the-point-of-promises/)
006 for a general introduction to promises.
007
008 - [Features](#features)
009 - [Quick start](#quick-start)
010 - [Synchronous wait](#synchronous-wait)
011 - [Cancellation](#cancellation)
012 - [API](#api)
013 - [Promise](#promise)
014 - [FulfilledPromise](#fulfilledpromise)
015 - [RejectedPromise](#rejectedpromise)
016 - [Promise interop](#promise-interop)
017 - [Implementation notes](#implementation-notes)
018
019
020 ## Features
021
022 - [Promises/A+](https://promisesaplus.com/) implementation.
023 - Promise resolution and chaining is handled iteratively, allowing for
024 "infinite" promise chaining.
025 - Promises have a synchronous `wait` method.
026 - Promises can be cancelled.
027 - Works with any object that has a `then` function.
028 - C# style async/await coroutine promises using
029 `GuzzleHttp\Promise\Coroutine::of()`.
030
031
032 ## Quick Start
033
034 A *promise* represents the eventual result of an asynchronous operation. The
035 primary way of interacting with a promise is through its `then` method, which
036 registers callbacks to receive either a promise's eventual value or the reason
037 why the promise cannot be fulfilled.
038
039 ### Callbacks
040
041 Callbacks are registered with the `then` method by providing an optional
042 `$onFulfilled` followed by an optional `$onRejected` function.
043
044
045 ```php
046 use GuzzleHttp\Promise\Promise;
047
048 $promise = new Promise();
049 $promise->then(
050 // $onFulfilled
051 function ($value) {
052 echo 'The promise was fulfilled.';
053 },
054 // $onRejected
055 function ($reason) {
056 echo 'The promise was rejected.';
057 }
058 );
059 ```
060
061 *Resolving* a promise means that you either fulfill a promise with a *value* or
062 reject a promise with a *reason*. Resolving a promise triggers callbacks
063 registered with the promise's `then` method. These callbacks are triggered
064 only once and in the order in which they were added.
065
066 ### Resolving a Promise
067
068 Promises are fulfilled using the `resolve($value)` method. Resolving a promise
069 with any value other than a `GuzzleHttp\Promise\RejectedPromise` will trigger
070 all of the onFulfilled callbacks (resolving a promise with a rejected promise
071 will reject the promise and trigger the `$onRejected` callbacks).
072
073 ```php
074 use GuzzleHttp\Promise\Promise;
075
076 $promise = new Promise();
077 $promise
078 ->then(function ($value) {
079 // Return a value and don't break the chain
080 return "Hello, " . $value;
081 })
082 // This then is executed after the first then and receives the value
083 // returned from the first then.
084 ->then(function ($value) {
085 echo $value;
086 });
087
088 // Resolving the promise triggers the $onFulfilled callbacks and outputs
089 // "Hello, reader."
090 $promise->resolve('reader.');
091 ```
092
093 ### Promise Forwarding
094
095 Promises can be chained one after the other. Each then in the chain is a new
096 promise. The return value of a promise is what's forwarded to the next
097 promise in the chain. Returning a promise in a `then` callback will cause the
098 subsequent promises in the chain to only be fulfilled when the returned promise
099 has been fulfilled. The next promise in the chain will be invoked with the
100 resolved value of the promise.
101
102 ```php
103 use GuzzleHttp\Promise\Promise;
104
105 $promise = new Promise();
106 $nextPromise = new Promise();
107
108 $promise
109 ->then(function ($value) use ($nextPromise) {
110 echo $value;
111 return $nextPromise;
112 })
113 ->then(function ($value) {
114 echo $value;
115 });
116
117 // Triggers the first callback and outputs "A"
118 $promise->resolve('A');
119 // Triggers the second callback and outputs "B"
120 $nextPromise->resolve('B');
121 ```
122
123 ### Promise Rejection
124
125 When a promise is rejected, the `$onRejected` callbacks are invoked with the
126 rejection reason.
127
128 ```php
129 use GuzzleHttp\Promise\Promise;
130
131 $promise = new Promise();
132 $promise->then(null, function ($reason) {
133 echo $reason;
134 });
135
136 $promise->reject('Error!');
137 // Outputs "Error!"
138 ```
139
140 ### Rejection Forwarding
141
142 If an exception is thrown in an `$onRejected` callback, subsequent
143 `$onRejected` callbacks are invoked with the thrown exception as the reason.
144
145 ```php
146 use GuzzleHttp\Promise\Promise;
147
148 $promise = new Promise();
149 $promise->then(null, function ($reason) {
150 throw new Exception($reason);
151 })->then(null, function ($reason) {
152 assert($reason->getMessage() === 'Error!');
153 });
154
155 $promise->reject('Error!');
156 ```
157
158 You can also forward a rejection down the promise chain by returning a
159 `GuzzleHttp\Promise\RejectedPromise` in either an `$onFulfilled` or
160 `$onRejected` callback.
161
162 ```php
163 use GuzzleHttp\Promise\Promise;
164 use GuzzleHttp\Promise\RejectedPromise;
165
166 $promise = new Promise();
167 $promise->then(null, function ($reason) {
168 return new RejectedPromise($reason);
169 })->then(null, function ($reason) {
170 assert($reason === 'Error!');
171 });
172
173 $promise->reject('Error!');
174 ```
175
176 If an exception is not thrown in a `$onRejected` callback and the callback
177 does not return a rejected promise, downstream `$onFulfilled` callbacks are
178 invoked using the value returned from the `$onRejected` callback.
179
180 ```php
181 use GuzzleHttp\Promise\Promise;
182
183 $promise = new Promise();
184 $promise
185 ->then(null, function ($reason) {
186 return "It's ok";
187 })
188 ->then(function ($value) {
189 assert($value === "It's ok");
190 });
191
192 $promise->reject('Error!');
193 ```
194
195
196 ## Synchronous Wait
197
198 You can synchronously force promises to complete using a promise's `wait`
199 method. When creating a promise, you can provide a wait function that is used
200 to synchronously force a promise to complete. When a wait function is invoked
201 it is expected to deliver a value to the promise or reject the promise. If the
202 wait function does not deliver a value, then an exception is thrown. The wait
203 function provided to a promise constructor is invoked when the `wait` function
204 of the promise is called.
205
206 ```php
207 $promise = new Promise(function () use (&$promise) {
208 $promise->resolve('foo');
209 });
210
211 // Calling wait will return the value of the promise.
212 echo $promise->wait(); // outputs "foo"
213 ```
214
215 If an exception is encountered while invoking the wait function of a promise,
216 the promise is rejected with the exception and the exception is thrown.
217
218 ```php
219 $promise = new Promise(function () use (&$promise) {
220 throw new Exception('foo');
221 });
222
223 $promise->wait(); // throws the exception.
224 ```
225
226 Calling `wait` on a promise that has been fulfilled will not trigger the wait
227 function. It will simply return the previously resolved value.
228
229 ```php
230 $promise = new Promise(function () { die('this is not called!'); });
231 $promise->resolve('foo');
232 echo $promise->wait(); // outputs "foo"
233 ```
234
235 Calling `wait` on a promise that has been rejected will throw an exception. If
236 the rejection reason is an instance of `\Exception` the reason is thrown.
237 Otherwise, a `GuzzleHttp\Promise\RejectionException` is thrown and the reason
238 can be obtained by calling the `getReason` method of the exception.
239
240 ```php
241 $promise = new Promise();
242 $promise->reject('foo');
243 $promise->wait();
244 ```
245
246 > PHP Fatal error: Uncaught exception 'GuzzleHttp\Promise\RejectionException' with message 'The promise was rejected with value: foo'
247
248 ### Unwrapping a Promise
249
250 When synchronously waiting on a promise, you are joining the state of the
251 promise into the current state of execution (i.e., return the value of the
252 promise if it was fulfilled or throw an exception if it was rejected). This is
253 called "unwrapping" the promise. Waiting on a promise will by default unwrap
254 the promise state.
255
256 You can force a promise to resolve and *not* unwrap the state of the promise
257 by passing `false` to the first argument of the `wait` function:
258
259 ```php
260 $promise = new Promise();
261 $promise->reject('foo');
262 // This will not throw an exception. It simply ensures the promise has
263 // been resolved.
264 $promise->wait(false);
265 ```
266
267 When unwrapping a promise, the resolved value of the promise will be waited
268 upon until the unwrapped value is not a promise. This means that if you resolve
269 promise A with a promise B and unwrap promise A, the value returned by the
270 wait function will be the value delivered to promise B.
271
272 **Note**: when you do not unwrap the promise, no value is returned.
273
274
275 ## Cancellation
276
277 You can cancel a promise that has not yet been fulfilled using the `cancel()`
278 method of a promise. When creating a promise you can provide an optional
279 cancel function that when invoked cancels the action of computing a resolution
280 of the promise.
281
282
283 ## API
284
285 ### Promise
286
287 When creating a promise object, you can provide an optional `$waitFn` and
288 `$cancelFn`. `$waitFn` is a function that is invoked with no arguments and is
289 expected to resolve the promise. `$cancelFn` is a function with no arguments
290 that is expected to cancel the computation of a promise. It is invoked when the
291 `cancel()` method of a promise is called.
292
293 ```php
294 use GuzzleHttp\Promise\Promise;
295
296 $promise = new Promise(
297 function () use (&$promise) {
298 $promise->resolve('waited');
299 },
300 function () {
301 // do something that will cancel the promise computation (e.g., close
302 // a socket, cancel a database query, etc...)
303 }
304 );
305
306 assert('waited' === $promise->wait());
307 ```
308
309 A promise has the following methods:
310
311 - `then(callable $onFulfilled, callable $onRejected) : PromiseInterface`
312
313 Appends fulfillment and rejection handlers to the promise, and returns a new promise resolving to the return value of the called handler.
314
315 - `otherwise(callable $onRejected) : PromiseInterface`
316
317 Appends a rejection handler callback to the promise, and returns a new promise resolving to the return value of the callback if it is called, or to its original fulfillment value if the promise is instead fulfilled.
318
319 - `wait($unwrap = true) : mixed`
320
321 Synchronously waits on the promise to complete.
322
323 `$unwrap` controls whether or not the value of the promise is returned for a
324 fulfilled promise or if an exception is thrown if the promise is rejected.
325 This is set to `true` by default.
326
327 - `cancel()`
328
329 Attempts to cancel the promise if possible. The promise being cancelled and
330 the parent most ancestor that has not yet been resolved will also be
331 cancelled. Any promises waiting on the cancelled promise to resolve will also
332 be cancelled.
333
334 - `getState() : string`
335
336 Returns the state of the promise. One of `pending`, `fulfilled`, or
337 `rejected`.
338
339 - `resolve($value)`
340
341 Fulfills the promise with the given `$value`.
342
343 - `reject($reason)`
344
345 Rejects the promise with the given `$reason`.
346
347
348 ### FulfilledPromise
349
350 A fulfilled promise can be created to represent a promise that has been
351 fulfilled.
352
353 ```php
354 use GuzzleHttp\Promise\FulfilledPromise;
355
356 $promise = new FulfilledPromise('value');
357
358 // Fulfilled callbacks are immediately invoked.
359 $promise->then(function ($value) {
360 echo $value;
361 });
362 ```
363
364
365 ### RejectedPromise
366
367 A rejected promise can be created to represent a promise that has been
368 rejected.
369
370 ```php
371 use GuzzleHttp\Promise\RejectedPromise;
372
373 $promise = new RejectedPromise('Error');
374
375 // Rejected callbacks are immediately invoked.
376 $promise->then(null, function ($reason) {
377 echo $reason;
378 });
379 ```
380
381
382 ## Promise Interoperability
383
384 This library works with foreign promises that have a `then` method. This means
385 you can use Guzzle promises with [React promises](https://github.com/reactphp/promise)
386 for example. When a foreign promise is returned inside of a then method
387 callback, promise resolution will occur recursively.
388
389 ```php
390 // Create a React promise
391 $deferred = new React\Promise\Deferred();
392 $reactPromise = $deferred->promise();
393
394 // Create a Guzzle promise that is fulfilled with a React promise.
395 $guzzlePromise = new GuzzleHttp\Promise\Promise();
396 $guzzlePromise->then(function ($value) use ($reactPromise) {
397 // Do something something with the value...
398 // Return the React promise
399 return $reactPromise;
400 });
401 ```
402
403 Please note that wait and cancel chaining is no longer possible when forwarding
404 a foreign promise. You will need to wrap a third-party promise with a Guzzle
405 promise in order to utilize wait and cancel functions with foreign promises.
406
407
408 ### Event Loop Integration
409
410 In order to keep the stack size constant, Guzzle promises are resolved
411 asynchronously using a task queue. When waiting on promises synchronously, the
412 task queue will be automatically run to ensure that the blocking promise and
413 any forwarded promises are resolved. When using promises asynchronously in an
414 event loop, you will need to run the task queue on each tick of the loop. If
415 you do not run the task queue, then promises will not be resolved.
416
417 You can run the task queue using the `run()` method of the global task queue
418 instance.
419
420 ```php
421 // Get the global task queue
422 $queue = GuzzleHttp\Promise\Utils::queue();
423 $queue->run();
424 ```
425
426 For example, you could use Guzzle promises with React using a periodic timer:
427
428 ```php
429 $loop = React\EventLoop\Factory::create();
430 $loop->addPeriodicTimer(0, [$queue, 'run']);
431 ```
432
433 *TODO*: Perhaps adding a `futureTick()` on each tick would be faster?
434
435
436 ## Implementation Notes
437
438 ### Promise Resolution and Chaining is Handled Iteratively
439
440 By shuffling pending handlers from one owner to another, promises are
441 resolved iteratively, allowing for "infinite" then chaining.
442
443 ```php
444 <?php
445 require 'vendor/autoload.php';
446
447 use GuzzleHttp\Promise\Promise;
448
449 $parent = new Promise();
450 $p = $parent;
451
452 for ($i = 0; $i < 1000; $i++) {
453 $p = $p->then(function ($v) {
454 // The stack size remains constant (a good thing)
455 echo xdebug_get_stack_depth() . ', ';
456 return $v + 1;
457 });
458 }
459
460 $parent->resolve(0);
461 var_dump($p->wait()); // int(1000)
462
463 ```
464
465 When a promise is fulfilled or rejected with a non-promise value, the promise
466 then takes ownership of the handlers of each child promise and delivers values
467 down the chain without using recursion.
468
469 When a promise is resolved with another promise, the original promise transfers
470 all of its pending handlers to the new promise. When the new promise is
471 eventually resolved, all of the pending handlers are delivered the forwarded
472 value.
473
474 ### A Promise is the Deferred
475
476 Some promise libraries implement promises using a deferred object to represent
477 a computation and a promise object to represent the delivery of the result of
478 the computation. This is a nice separation of computation and delivery because
479 consumers of the promise cannot modify the value that will be eventually
480 delivered.
481
482 One side effect of being able to implement promise resolution and chaining
483 iteratively is that you need to be able for one promise to reach into the state
484 of another promise to shuffle around ownership of handlers. In order to achieve
485 this without making the handlers of a promise publicly mutable, a promise is
486 also the deferred value, allowing promises of the same parent class to reach
487 into and modify the private properties of promises of the same type. While this
488 does allow consumers of the value to modify the resolution or rejection of the
489 deferred, it is a small price to pay for keeping the stack size constant.
490
491 ```php
492 $promise = new Promise();
493 $promise->then(function ($value) { echo $value; });
494 // The promise is the deferred value, so you can deliver a value to it.
495 $promise->resolve('foo');
496 // prints "foo"
497 ```
498
499
500 ## Upgrading from Function API
501
502 A static API was first introduced in 1.4.0, in order to mitigate problems with
503 functions conflicting between global and local copies of the package. The
504 function API will be removed in 2.0.0. A migration table has been provided here
505 for your convenience:
506
507 | Original Function | Replacement Method |
508 |----------------|----------------|
509 | `queue` | `Utils::queue` |
510 | `task` | `Utils::task` |
511 | `promise_for` | `Create::promiseFor` |
512 | `rejection_for` | `Create::rejectionFor` |
513 | `exception_for` | `Create::exceptionFor` |
514 | `iter_for` | `Create::iterFor` |
515 | `inspect` | `Utils::inspect` |
516 | `inspect_all` | `Utils::inspectAll` |
517 | `unwrap` | `Utils::unwrap` |
518 | `all` | `Utils::all` |
519 | `some` | `Utils::some` |
520 | `any` | `Utils::any` |
521 | `settle` | `Utils::settle` |
522 | `each` | `Each::of` |
523 | `each_limit` | `Each::ofLimit` |
524 | `each_limit_all` | `Each::ofLimitAll` |
525 | `!is_fulfilled` | `Is::pending` |
526 | `is_fulfilled` | `Is::fulfilled` |
527 | `is_rejected` | `Is::rejected` |
528 | `is_settled` | `Is::settled` |
529 | `coroutine` | `Coroutine::of` |
530
531
532 ## Security
533
534 If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see [Security Policy](https://github.com/guzzle/promises/security/policy) for more information.
535
536
537 ## License
538
539 Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information.
540
541
542 ## For Enterprise
543
544 Available as part of the Tidelift Subscription
545
546 The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-guzzlehttp-promises?utm_source=packagist-guzzlehttp-promises&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
547