dokuwiki-matrixnotifierwas-plugin – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 # Guzzle Promises
2  
3 [Promises/A+](https://promisesaplus.com/) implementation that handles promise
4 chaining and resolution iteratively, allowing for "infinite" promise chaining
5 while keeping the stack size constant. Read [this blog post](https://blog.domenic.me/youre-missing-the-point-of-promises/)
6 for 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
35 composer require guzzlehttp/promises
36 ```
37  
38  
39 ## Version Guidance
40  
41 | Version | Status | PHP Version |
42 |---------|------------------------|--------------|
43 | 1.x | Bug and security fixes | >=5.5,<8.3 |
44 | 2.x | Latest | >=7.2.5,<8.4 |
45  
46  
47 ## Quick Start
48  
49 A *promise* represents the eventual result of an asynchronous operation. The
50 primary way of interacting with a promise is through its `then` method, which
51 registers callbacks to receive either a promise's eventual value or the reason
52 why the promise cannot be fulfilled.
53  
54 ### Callbacks
55  
56 Callbacks are registered with the `then` method by providing an optional
57 `$onFulfilled` followed by an optional `$onRejected` function.
58  
59  
60 ```php
61 use 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
77 reject a promise with a *reason*. Resolving a promise triggers callbacks
78 registered with the promise's `then` method. These callbacks are triggered
79 only once and in the order in which they were added.
80  
81 ### Resolving a Promise
82  
83 Promises are fulfilled using the `resolve($value)` method. Resolving a promise
84 with any value other than a `GuzzleHttp\Promise\RejectedPromise` will trigger
85 all of the onFulfilled callbacks (resolving a promise with a rejected promise
86 will reject the promise and trigger the `$onRejected` callbacks).
87  
88 ```php
89 use 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  
110 Promises can be chained one after the other. Each then in the chain is a new
111 promise. The return value of a promise is what's forwarded to the next
112 promise in the chain. Returning a promise in a `then` callback will cause the
113 subsequent promises in the chain to only be fulfilled when the returned promise
114 has been fulfilled. The next promise in the chain will be invoked with the
115 resolved value of the promise.
116  
117 ```php
118 use 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  
140 When a promise is rejected, the `$onRejected` callbacks are invoked with the
141 rejection reason.
142  
143 ```php
144 use 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  
157 If an exception is thrown in an `$onRejected` callback, subsequent
158 `$onRejected` callbacks are invoked with the thrown exception as the reason.
159  
160 ```php
161 use 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  
173 You 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
178 use GuzzleHttp\Promise\Promise;
179 use 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  
191 If an exception is not thrown in a `$onRejected` callback and the callback
192 does not return a rejected promise, downstream `$onFulfilled` callbacks are
193 invoked using the value returned from the `$onRejected` callback.
194  
195 ```php
196 use 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  
213 You can synchronously force promises to complete using a promise's `wait`
214 method. When creating a promise, you can provide a wait function that is used
215 to synchronously force a promise to complete. When a wait function is invoked
216 it is expected to deliver a value to the promise or reject the promise. If the
217 wait function does not deliver a value, then an exception is thrown. The wait
218 function provided to a promise constructor is invoked when the `wait` function
219 of 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.
227 echo $promise->wait(); // outputs "foo"
228 ```
229  
230 If an exception is encountered while invoking the wait function of a promise,
231 the 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  
241 Calling `wait` on a promise that has been fulfilled will not trigger the wait
242 function. 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');
247 echo $promise->wait(); // outputs "foo"
248 ```
249  
250 Calling `wait` on a promise that has been rejected will throw an exception. If
251 the rejection reason is an instance of `\Exception` the reason is thrown.
252 Otherwise, a `GuzzleHttp\Promise\RejectionException` is thrown and the reason
253 can 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  
265 When synchronously waiting on a promise, you are joining the state of the
266 promise into the current state of execution (i.e., return the value of the
267 promise if it was fulfilled or throw an exception if it was rejected). This is
268 called "unwrapping" the promise. Waiting on a promise will by default unwrap
269 the promise state.
270  
271 You can force a promise to resolve and *not* unwrap the state of the promise
272 by 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  
282 When unwrapping a promise, the resolved value of the promise will be waited
283 upon until the unwrapped value is not a promise. This means that if you resolve
284 promise A with a promise B and unwrap promise A, the value returned by the
285 wait 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  
292 You can cancel a promise that has not yet been fulfilled using the `cancel()`
293 method of a promise. When creating a promise you can provide an optional
294 cancel function that when invoked cancels the action of computing a resolution
295 of the promise.
296  
297  
298 ## API
299  
300 ### Promise
301  
302 When 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
304 expected to resolve the promise. `$cancelFn` is a function with no arguments
305 that 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
309 use 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  
321 assert('waited' === $promise->wait());
322 ```
323  
324 A 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  
365 A fulfilled promise can be created to represent a promise that has been
366 fulfilled.
367  
368 ```php
369 use 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  
382 A rejected promise can be created to represent a promise that has been
383 rejected.
384  
385 ```php
386 use 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  
399 This library works with foreign promises that have a `then` method. This means
400 you can use Guzzle promises with [React promises](https://github.com/reactphp/promise)
401 for example. When a foreign promise is returned inside of a then method
402 callback, 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  
418 Please note that wait and cancel chaining is no longer possible when forwarding
419 a foreign promise. You will need to wrap a third-party promise with a Guzzle
420 promise in order to utilize wait and cancel functions with foreign promises.
421  
422  
423 ### Event Loop Integration
424  
425 In order to keep the stack size constant, Guzzle promises are resolved
426 asynchronously using a task queue. When waiting on promises synchronously, the
427 task queue will be automatically run to ensure that the blocking promise and
428 any forwarded promises are resolved. When using promises asynchronously in an
429 event loop, you will need to run the task queue on each tick of the loop. If
430 you do not run the task queue, then promises will not be resolved.
431  
432 You can run the task queue using the `run()` method of the global task queue
433 instance.
434  
435 ```php
436 // Get the global task queue
437 $queue = GuzzleHttp\Promise\Utils::queue();
438 $queue->run();
439 ```
440  
441 For 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  
453 By shuffling pending handlers from one owner to another, promises are
454 resolved iteratively, allowing for "infinite" then chaining.
455  
456 ```php
457 <?php
458 require 'vendor/autoload.php';
459  
460 use GuzzleHttp\Promise\Promise;
461  
462 $parent = new Promise();
463 $p = $parent;
464  
465 for ($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);
474 var_dump($p->wait()); // int(1000)
475  
476 ```
477  
478 When a promise is fulfilled or rejected with a non-promise value, the promise
479 then takes ownership of the handlers of each child promise and delivers values
480 down the chain without using recursion.
481  
482 When a promise is resolved with another promise, the original promise transfers
483 all of its pending handlers to the new promise. When the new promise is
484 eventually resolved, all of the pending handlers are delivered the forwarded
485 value.
486  
487 ### A Promise is the Deferred
488  
489 Some promise libraries implement promises using a deferred object to represent
490 a computation and a promise object to represent the delivery of the result of
491 the computation. This is a nice separation of computation and delivery because
492 consumers of the promise cannot modify the value that will be eventually
493 delivered.
494  
495 One side effect of being able to implement promise resolution and chaining
496 iteratively is that you need to be able for one promise to reach into the state
497 of another promise to shuffle around ownership of handlers. In order to achieve
498 this without making the handlers of a promise publicly mutable, a promise is
499 also the deferred value, allowing promises of the same parent class to reach
500 into and modify the private properties of promises of the same type. While this
501 does allow consumers of the value to modify the resolution or rejection of the
502 deferred, 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  
515 A static API was first introduced in 1.4.0, in order to mitigate problems with
516 functions conflicting between global and local copies of the package. The
517 function API was removed in 2.0.0. A migration table has been provided here for
518 your 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  
547 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.
548  
549  
550 ## License
551  
552 Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information.
553  
554  
555 ## For Enterprise
556  
557 Available as part of the Tidelift Subscription
558  
559 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)