[ Index ] |
PHP Cross Reference of phpBB-3.3.14-deutsch |
[Summary view] [Print] [Text view]
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 ## Quick Start 33 34 A *promise* represents the eventual result of an asynchronous operation. The 35 primary way of interacting with a promise is through its `then` method, which 36 registers callbacks to receive either a promise's eventual value or the reason 37 why the promise cannot be fulfilled. 38 39 ### Callbacks 40 41 Callbacks are registered with the `then` method by providing an optional 42 `$onFulfilled` followed by an optional `$onRejected` function. 43 44 45 ```php 46 use GuzzleHttp\Promise\Promise; 47 48 $promise = new Promise(); 49 $promise->then( 50 // $onFulfilled 51 function ($value) { 52 echo 'The promise was fulfilled.'; 53 }, 54 // $onRejected 55 function ($reason) { 56 echo 'The promise was rejected.'; 57 } 58 ); 59 ``` 60 61 *Resolving* a promise means that you either fulfill a promise with a *value* or 62 reject a promise with a *reason*. Resolving a promise triggers callbacks 63 registered with the promise's `then` method. These callbacks are triggered 64 only once and in the order in which they were added. 65 66 ### Resolving a Promise 67 68 Promises are fulfilled using the `resolve($value)` method. Resolving a promise 69 with any value other than a `GuzzleHttp\Promise\RejectedPromise` will trigger 70 all of the onFulfilled callbacks (resolving a promise with a rejected promise 71 will reject the promise and trigger the `$onRejected` callbacks). 72 73 ```php 74 use GuzzleHttp\Promise\Promise; 75 76 $promise = new Promise(); 77 $promise 78 ->then(function ($value) { 79 // Return a value and don't break the chain 80 return "Hello, " . $value; 81 }) 82 // This then is executed after the first then and receives the value 83 // returned from the first then. 84 ->then(function ($value) { 85 echo $value; 86 }); 87 88 // Resolving the promise triggers the $onFulfilled callbacks and outputs 89 // "Hello, reader." 90 $promise->resolve('reader.'); 91 ``` 92 93 ### Promise Forwarding 94 95 Promises can be chained one after the other. Each then in the chain is a new 96 promise. The return value of a promise is what's forwarded to the next 97 promise in the chain. Returning a promise in a `then` callback will cause the 98 subsequent promises in the chain to only be fulfilled when the returned promise 99 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)
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Mon Nov 25 19:05:08 2024 | Cross-referenced by PHPXref 0.7.1 |