source: vendor/guzzlehttp/promises/README.md@ e3d4e0a

Last change on this file since e3d4e0a was e3d4e0a, checked in by Vlado 222039 <vlado.popovski@…>, 7 days ago

Upload project files

  • Property mode set to 100644
File size: 17.2 KB
RevLine 
[e3d4e0a]1# Guzzle Promises
2
3[Promises/A+](https://promisesaplus.com/) implementation that handles promise
4chaining and resolution iteratively, allowing for "infinite" promise chaining
5while keeping the stack size constant. Read [this blog post](https://blog.domenic.me/youre-missing-the-point-of-promises/)
6for a general introduction to promises.
7
8- [Features](#features)
9- [Quick start](#quick-start)
10- [Synchronous wait](#synchronous-wait)
11- [Cancellation](#cancellation)
12- [API](#api)
13 - [Promise](#promise)
14 - [FulfilledPromise](#fulfilledpromise)
15 - [RejectedPromise](#rejectedpromise)
16- [Promise interop](#promise-interop)
17- [Implementation notes](#implementation-notes)
18
19
20## Features
21
22- [Promises/A+](https://promisesaplus.com/) implementation.
23- Promise resolution and chaining is handled iteratively, allowing for
24 "infinite" promise chaining.
25- Promises have a synchronous `wait` method.
26- Promises can be cancelled.
27- Works with any object that has a `then` function.
28- C# style async/await coroutine promises using
29 `GuzzleHttp\Promise\Coroutine::of()`.
30
31
32## Installation
33
34```shell
35composer require guzzlehttp/promises
36```
37
38
39## Version Guidance
40
41| Version | Status | PHP Version |
42|---------|---------------------|--------------|
43| 1.x | Security fixes only | >=5.5,<8.3 |
44| 2.x | Latest | >=7.2.5,<8.5 |
45
46
47## Quick Start
48
49A *promise* represents the eventual result of an asynchronous operation. The
50primary way of interacting with a promise is through its `then` method, which
51registers callbacks to receive either a promise's eventual value or the reason
52why the promise cannot be fulfilled.
53
54### Callbacks
55
56Callbacks are registered with the `then` method by providing an optional
57`$onFulfilled` followed by an optional `$onRejected` function.
58
59
60```php
61use GuzzleHttp\Promise\Promise;
62
63$promise = new Promise();
64$promise->then(
65 // $onFulfilled
66 function ($value) {
67 echo 'The promise was fulfilled.';
68 },
69 // $onRejected
70 function ($reason) {
71 echo 'The promise was rejected.';
72 }
73);
74```
75
76*Resolving* a promise means that you either fulfill a promise with a *value* or
77reject a promise with a *reason*. Resolving a promise triggers callbacks
78registered with the promise's `then` method. These callbacks are triggered
79only once and in the order in which they were added.
80
81### Resolving a Promise
82
83Promises are fulfilled using the `resolve($value)` method. Resolving a promise
84with any value other than a `GuzzleHttp\Promise\RejectedPromise` will trigger
85all of the onFulfilled callbacks (resolving a promise with a rejected promise
86will reject the promise and trigger the `$onRejected` callbacks).
87
88```php
89use GuzzleHttp\Promise\Promise;
90
91$promise = new Promise();
92$promise
93 ->then(function ($value) {
94 // Return a value and don't break the chain
95 return "Hello, " . $value;
96 })
97 // This then is executed after the first then and receives the value
98 // returned from the first then.
99 ->then(function ($value) {
100 echo $value;
101 });
102
103// Resolving the promise triggers the $onFulfilled callbacks and outputs
104// "Hello, reader."
105$promise->resolve('reader.');
106```
107
108### Promise Forwarding
109
110Promises can be chained one after the other. Each then in the chain is a new
111promise. The return value of a promise is what's forwarded to the next
112promise in the chain. Returning a promise in a `then` callback will cause the
113subsequent promises in the chain to only be fulfilled when the returned promise
114has been fulfilled. The next promise in the chain will be invoked with the
115resolved value of the promise.
116
117```php
118use GuzzleHttp\Promise\Promise;
119
120$promise = new Promise();
121$nextPromise = new Promise();
122
123$promise
124 ->then(function ($value) use ($nextPromise) {
125 echo $value;
126 return $nextPromise;
127 })
128 ->then(function ($value) {
129 echo $value;
130 });
131
132// Triggers the first callback and outputs "A"
133$promise->resolve('A');
134// Triggers the second callback and outputs "B"
135$nextPromise->resolve('B');
136```
137
138### Promise Rejection
139
140When a promise is rejected, the `$onRejected` callbacks are invoked with the
141rejection reason.
142
143```php
144use GuzzleHttp\Promise\Promise;
145
146$promise = new Promise();
147$promise->then(null, function ($reason) {
148 echo $reason;
149});
150
151$promise->reject('Error!');
152// Outputs "Error!"
153```
154
155### Rejection Forwarding
156
157If an exception is thrown in an `$onRejected` callback, subsequent
158`$onRejected` callbacks are invoked with the thrown exception as the reason.
159
160```php
161use GuzzleHttp\Promise\Promise;
162
163$promise = new Promise();
164$promise->then(null, function ($reason) {
165 throw new Exception($reason);
166})->then(null, function ($reason) {
167 assert($reason->getMessage() === 'Error!');
168});
169
170$promise->reject('Error!');
171```
172
173You can also forward a rejection down the promise chain by returning a
174`GuzzleHttp\Promise\RejectedPromise` in either an `$onFulfilled` or
175`$onRejected` callback.
176
177```php
178use GuzzleHttp\Promise\Promise;
179use GuzzleHttp\Promise\RejectedPromise;
180
181$promise = new Promise();
182$promise->then(null, function ($reason) {
183 return new RejectedPromise($reason);
184})->then(null, function ($reason) {
185 assert($reason === 'Error!');
186});
187
188$promise->reject('Error!');
189```
190
191If an exception is not thrown in a `$onRejected` callback and the callback
192does not return a rejected promise, downstream `$onFulfilled` callbacks are
193invoked using the value returned from the `$onRejected` callback.
194
195```php
196use GuzzleHttp\Promise\Promise;
197
198$promise = new Promise();
199$promise
200 ->then(null, function ($reason) {
201 return "It's ok";
202 })
203 ->then(function ($value) {
204 assert($value === "It's ok");
205 });
206
207$promise->reject('Error!');
208```
209
210
211## Synchronous Wait
212
213You can synchronously force promises to complete using a promise's `wait`
214method. When creating a promise, you can provide a wait function that is used
215to synchronously force a promise to complete. When a wait function is invoked
216it is expected to deliver a value to the promise or reject the promise. If the
217wait function does not deliver a value, then an exception is thrown. The wait
218function provided to a promise constructor is invoked when the `wait` function
219of the promise is called.
220
221```php
222$promise = new Promise(function () use (&$promise) {
223 $promise->resolve('foo');
224});
225
226// Calling wait will return the value of the promise.
227echo $promise->wait(); // outputs "foo"
228```
229
230If an exception is encountered while invoking the wait function of a promise,
231the promise is rejected with the exception and the exception is thrown.
232
233```php
234$promise = new Promise(function () use (&$promise) {
235 throw new Exception('foo');
236});
237
238$promise->wait(); // throws the exception.
239```
240
241Calling `wait` on a promise that has been fulfilled will not trigger the wait
242function. It will simply return the previously resolved value.
243
244```php
245$promise = new Promise(function () { die('this is not called!'); });
246$promise->resolve('foo');
247echo $promise->wait(); // outputs "foo"
248```
249
250Calling `wait` on a promise that has been rejected will throw an exception. If
251the rejection reason is an instance of `\Exception` the reason is thrown.
252Otherwise, a `GuzzleHttp\Promise\RejectionException` is thrown and the reason
253can be obtained by calling the `getReason` method of the exception.
254
255```php
256$promise = new Promise();
257$promise->reject('foo');
258$promise->wait();
259```
260
261> PHP Fatal error: Uncaught exception 'GuzzleHttp\Promise\RejectionException' with message 'The promise was rejected with value: foo'
262
263### Unwrapping a Promise
264
265When synchronously waiting on a promise, you are joining the state of the
266promise into the current state of execution (i.e., return the value of the
267promise if it was fulfilled or throw an exception if it was rejected). This is
268called "unwrapping" the promise. Waiting on a promise will by default unwrap
269the promise state.
270
271You can force a promise to resolve and *not* unwrap the state of the promise
272by passing `false` to the first argument of the `wait` function:
273
274```php
275$promise = new Promise();
276$promise->reject('foo');
277// This will not throw an exception. It simply ensures the promise has
278// been resolved.
279$promise->wait(false);
280```
281
282When unwrapping a promise, the resolved value of the promise will be waited
283upon until the unwrapped value is not a promise. This means that if you resolve
284promise A with a promise B and unwrap promise A, the value returned by the
285wait function will be the value delivered to promise B.
286
287**Note**: when you do not unwrap the promise, no value is returned.
288
289
290## Cancellation
291
292You can cancel a promise that has not yet been fulfilled using the `cancel()`
293method of a promise. When creating a promise you can provide an optional
294cancel function that when invoked cancels the action of computing a resolution
295of the promise.
296
297
298## API
299
300### Promise
301
302When creating a promise object, you can provide an optional `$waitFn` and
303`$cancelFn`. `$waitFn` is a function that is invoked with no arguments and is
304expected to resolve the promise. `$cancelFn` is a function with no arguments
305that is expected to cancel the computation of a promise. It is invoked when the
306`cancel()` method of a promise is called.
307
308```php
309use GuzzleHttp\Promise\Promise;
310
311$promise = new Promise(
312 function () use (&$promise) {
313 $promise->resolve('waited');
314 },
315 function () {
316 // do something that will cancel the promise computation (e.g., close
317 // a socket, cancel a database query, etc...)
318 }
319);
320
321assert('waited' === $promise->wait());
322```
323
324A promise has the following methods:
325
326- `then(callable $onFulfilled, callable $onRejected) : PromiseInterface`
327
328 Appends fulfillment and rejection handlers to the promise, and returns a new promise resolving to the return value of the called handler.
329
330- `otherwise(callable $onRejected) : PromiseInterface`
331
332 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.
333
334- `wait($unwrap = true) : mixed`
335
336 Synchronously waits on the promise to complete.
337
338 `$unwrap` controls whether or not the value of the promise is returned for a
339 fulfilled promise or if an exception is thrown if the promise is rejected.
340 This is set to `true` by default.
341
342- `cancel()`
343
344 Attempts to cancel the promise if possible. The promise being cancelled and
345 the parent most ancestor that has not yet been resolved will also be
346 cancelled. Any promises waiting on the cancelled promise to resolve will also
347 be cancelled.
348
349- `getState() : string`
350
351 Returns the state of the promise. One of `pending`, `fulfilled`, or
352 `rejected`.
353
354- `resolve($value)`
355
356 Fulfills the promise with the given `$value`.
357
358- `reject($reason)`
359
360 Rejects the promise with the given `$reason`.
361
362
363### FulfilledPromise
364
365A fulfilled promise can be created to represent a promise that has been
366fulfilled.
367
368```php
369use GuzzleHttp\Promise\FulfilledPromise;
370
371$promise = new FulfilledPromise('value');
372
373// Fulfilled callbacks are immediately invoked.
374$promise->then(function ($value) {
375 echo $value;
376});
377```
378
379
380### RejectedPromise
381
382A rejected promise can be created to represent a promise that has been
383rejected.
384
385```php
386use GuzzleHttp\Promise\RejectedPromise;
387
388$promise = new RejectedPromise('Error');
389
390// Rejected callbacks are immediately invoked.
391$promise->then(null, function ($reason) {
392 echo $reason;
393});
394```
395
396
397## Promise Interoperability
398
399This library works with foreign promises that have a `then` method. This means
400you can use Guzzle promises with [React promises](https://github.com/reactphp/promise)
401for example. When a foreign promise is returned inside of a then method
402callback, promise resolution will occur recursively.
403
404```php
405// Create a React promise
406$deferred = new React\Promise\Deferred();
407$reactPromise = $deferred->promise();
408
409// Create a Guzzle promise that is fulfilled with a React promise.
410$guzzlePromise = new GuzzleHttp\Promise\Promise();
411$guzzlePromise->then(function ($value) use ($reactPromise) {
412 // Do something something with the value...
413 // Return the React promise
414 return $reactPromise;
415});
416```
417
418Please note that wait and cancel chaining is no longer possible when forwarding
419a foreign promise. You will need to wrap a third-party promise with a Guzzle
420promise in order to utilize wait and cancel functions with foreign promises.
421
422
423### Event Loop Integration
424
425In order to keep the stack size constant, Guzzle promises are resolved
426asynchronously using a task queue. When waiting on promises synchronously, the
427task queue will be automatically run to ensure that the blocking promise and
428any forwarded promises are resolved. When using promises asynchronously in an
429event loop, you will need to run the task queue on each tick of the loop. If
430you do not run the task queue, then promises will not be resolved.
431
432You can run the task queue using the `run()` method of the global task queue
433instance.
434
435```php
436// Get the global task queue
437$queue = GuzzleHttp\Promise\Utils::queue();
438$queue->run();
439```
440
441For example, you could use Guzzle promises with React using a periodic timer:
442
443```php
444$loop = React\EventLoop\Factory::create();
445$loop->addPeriodicTimer(0, [$queue, 'run']);
446```
447
448
449## Implementation Notes
450
451### Promise Resolution and Chaining is Handled Iteratively
452
453By shuffling pending handlers from one owner to another, promises are
454resolved iteratively, allowing for "infinite" then chaining.
455
456```php
457<?php
458require 'vendor/autoload.php';
459
460use GuzzleHttp\Promise\Promise;
461
462$parent = new Promise();
463$p = $parent;
464
465for ($i = 0; $i < 1000; $i++) {
466 $p = $p->then(function ($v) {
467 // The stack size remains constant (a good thing)
468 echo xdebug_get_stack_depth() . ', ';
469 return $v + 1;
470 });
471}
472
473$parent->resolve(0);
474var_dump($p->wait()); // int(1000)
475
476```
477
478When a promise is fulfilled or rejected with a non-promise value, the promise
479then takes ownership of the handlers of each child promise and delivers values
480down the chain without using recursion.
481
482When a promise is resolved with another promise, the original promise transfers
483all of its pending handlers to the new promise. When the new promise is
484eventually resolved, all of the pending handlers are delivered the forwarded
485value.
486
487### A Promise is the Deferred
488
489Some promise libraries implement promises using a deferred object to represent
490a computation and a promise object to represent the delivery of the result of
491the computation. This is a nice separation of computation and delivery because
492consumers of the promise cannot modify the value that will be eventually
493delivered.
494
495One side effect of being able to implement promise resolution and chaining
496iteratively is that you need to be able for one promise to reach into the state
497of another promise to shuffle around ownership of handlers. In order to achieve
498this without making the handlers of a promise publicly mutable, a promise is
499also the deferred value, allowing promises of the same parent class to reach
500into and modify the private properties of promises of the same type. While this
501does allow consumers of the value to modify the resolution or rejection of the
502deferred, it is a small price to pay for keeping the stack size constant.
503
504```php
505$promise = new Promise();
506$promise->then(function ($value) { echo $value; });
507// The promise is the deferred value, so you can deliver a value to it.
508$promise->resolve('foo');
509// prints "foo"
510```
511
512
513## Upgrading from Function API
514
515A static API was first introduced in 1.4.0, in order to mitigate problems with
516functions conflicting between global and local copies of the package. The
517function API was removed in 2.0.0. A migration table has been provided here for
518your convenience:
519
520| Original Function | Replacement Method |
521|----------------|----------------|
522| `queue` | `Utils::queue` |
523| `task` | `Utils::task` |
524| `promise_for` | `Create::promiseFor` |
525| `rejection_for` | `Create::rejectionFor` |
526| `exception_for` | `Create::exceptionFor` |
527| `iter_for` | `Create::iterFor` |
528| `inspect` | `Utils::inspect` |
529| `inspect_all` | `Utils::inspectAll` |
530| `unwrap` | `Utils::unwrap` |
531| `all` | `Utils::all` |
532| `some` | `Utils::some` |
533| `any` | `Utils::any` |
534| `settle` | `Utils::settle` |
535| `each` | `Each::of` |
536| `each_limit` | `Each::ofLimit` |
537| `each_limit_all` | `Each::ofLimitAll` |
538| `!is_fulfilled` | `Is::pending` |
539| `is_fulfilled` | `Is::fulfilled` |
540| `is_rejected` | `Is::rejected` |
541| `is_settled` | `Is::settled` |
542| `coroutine` | `Coroutine::of` |
543
544
545## Security
546
547If 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.
548
549
550## License
551
552Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information.
553
554
555## For Enterprise
556
557Available as part of the Tidelift Subscription
558
559The 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)
Note: See TracBrowser for help on using the repository browser.