Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
 
1441 ariadna 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     | Security fixes only | >=5.5,<8.3   |
44
| 2.x     | Latest              | >=7.2.5,<8.5 |
45
 
46
 
1 efrain 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
1441 ariadna 517
function API was removed in 2.0.0. A migration table has been provided here for
518
your convenience:
1 efrain 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)