1Promise
2=======
3
4A lightweight implementation of
5[CommonJS Promises/A](http://wiki.commonjs.org/wiki/Promises/A) for PHP.
6
7[![CI status](https://github.com/reactphp/promise/actions/workflows/ci.yml/badge.svg?branch=2.x)](https://github.com/reactphp/promise/actions)
8[![installs on Packagist](https://img.shields.io/packagist/dt/react/promise?color=blue&label=installs%20on%20Packagist)](https://packagist.org/packages/react/promise)
9
10Table of Contents
11-----------------
12
131. [Introduction](#introduction)
142. [Concepts](#concepts)
15   * [Deferred](#deferred)
16   * [Promise](#promise-1)
173. [API](#api)
18   * [Deferred](#deferred-1)
19     * [Deferred::promise()](#deferredpromise)
20     * [Deferred::resolve()](#deferredresolve)
21     * [Deferred::reject()](#deferredreject)
22     * [Deferred::notify()](#deferrednotify)
23   * [PromiseInterface](#promiseinterface)
24     * [PromiseInterface::then()](#promiseinterfacethen)
25   * [ExtendedPromiseInterface](#extendedpromiseinterface)
26        * [ExtendedPromiseInterface::done()](#extendedpromiseinterfacedone)
27        * [ExtendedPromiseInterface::otherwise()](#extendedpromiseinterfaceotherwise)
28        * [ExtendedPromiseInterface::always()](#extendedpromiseinterfacealways)
29        * [ExtendedPromiseInterface::progress()](#extendedpromiseinterfaceprogress)
30   * [CancellablePromiseInterface](#cancellablepromiseinterface)
31        * [CancellablePromiseInterface::cancel()](#cancellablepromiseinterfacecancel)
32   * [Promise](#promise-2)
33   * [FulfilledPromise](#fulfilledpromise)
34   * [RejectedPromise](#rejectedpromise)
35   * [LazyPromise](#lazypromise)
36   * [Functions](#functions)
37     * [resolve()](#resolve)
38     * [reject()](#reject)
39     * [all()](#all)
40     * [race()](#race)
41     * [any()](#any)
42     * [some()](#some)
43     * [map()](#map)
44     * [reduce()](#reduce)
45   * [PromisorInterface](#promisorinterface)
464. [Examples](#examples)
47   * [How to use Deferred](#how-to-use-deferred)
48   * [How promise forwarding works](#how-promise-forwarding-works)
49     * [Resolution forwarding](#resolution-forwarding)
50     * [Rejection forwarding](#rejection-forwarding)
51     * [Mixed resolution and rejection forwarding](#mixed-resolution-and-rejection-forwarding)
52     * [Progress event forwarding](#progress-event-forwarding)
53   * [done() vs. then()](#done-vs-then)
545. [Install](#install)
556. [Credits](#credits)
567. [License](#license)
57
58Introduction
59------------
60
61Promise is a library implementing
62[CommonJS Promises/A](http://wiki.commonjs.org/wiki/Promises/A) for PHP.
63
64It also provides several other useful promise-related concepts, such as joining
65multiple promises and mapping and reducing collections of promises.
66
67If you've never heard about promises before,
68[read this first](https://gist.github.com/3889970).
69
70Concepts
71--------
72
73### Deferred
74
75A **Deferred** represents a computation or unit of work that may not have
76completed yet. Typically (but not always), that computation will be something
77that executes asynchronously and completes at some point in the future.
78
79### Promise
80
81While a deferred represents the computation itself, a **Promise** represents
82the result of that computation. Thus, each deferred has a promise that acts as
83a placeholder for its actual result.
84
85API
86---
87
88### Deferred
89
90A deferred represents an operation whose resolution is pending. It has separate
91promise and resolver parts.
92
93```php
94$deferred = new React\Promise\Deferred();
95
96$promise = $deferred->promise();
97
98$deferred->resolve(mixed $value = null);
99$deferred->reject(mixed $reason = null);
100$deferred->notify(mixed $update = null);
101```
102
103The `promise` method returns the promise of the deferred.
104
105The `resolve` and `reject` methods control the state of the deferred.
106
107The deprecated `notify` method is for progress notification.
108
109The constructor of the `Deferred` accepts an optional `$canceller` argument.
110See [Promise](#promise-2) for more information.
111
112#### Deferred::promise()
113
114```php
115$promise = $deferred->promise();
116```
117
118Returns the promise of the deferred, which you can hand out to others while
119keeping the authority to modify its state to yourself.
120
121#### Deferred::resolve()
122
123```php
124$deferred->resolve(mixed $value = null);
125```
126
127Resolves the promise returned by `promise()`. All consumers are notified by
128having `$onFulfilled` (which they registered via `$promise->then()`) called with
129`$value`.
130
131If `$value` itself is a promise, the promise will transition to the state of
132this promise once it is resolved.
133
134#### Deferred::reject()
135
136```php
137$deferred->reject(mixed $reason = null);
138```
139
140Rejects the promise returned by `promise()`, signalling that the deferred's
141computation failed.
142All consumers are notified by having `$onRejected` (which they registered via
143`$promise->then()`) called with `$reason`.
144
145If `$reason` itself is a promise, the promise will be rejected with the outcome
146of this promise regardless whether it fulfills or rejects.
147
148#### Deferred::notify()
149
150> Deprecated in v2.6.0: Progress support is deprecated and should not be used anymore.
151
152```php
153$deferred->notify(mixed $update = null);
154```
155
156Triggers progress notifications, to indicate to consumers that the computation
157is making progress toward its result.
158
159All consumers are notified by having `$onProgress` (which they registered via
160`$promise->then()`) called with `$update`.
161
162### PromiseInterface
163
164The promise interface provides the common interface for all promise
165implementations.
166
167A promise represents an eventual outcome, which is either fulfillment (success)
168and an associated value, or rejection (failure) and an associated reason.
169
170Once in the fulfilled or rejected state, a promise becomes immutable.
171Neither its state nor its result (or error) can be modified.
172
173#### Implementations
174
175* [Promise](#promise-2)
176* [FulfilledPromise](#fulfilledpromise) (deprecated)
177* [RejectedPromise](#rejectedpromise) (deprecated)
178* [LazyPromise](#lazypromise) (deprecated)
179
180#### PromiseInterface::then()
181
182```php
183$transformedPromise = $promise->then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null);
184```
185
186Transforms a promise's value by applying a function to the promise's fulfillment
187or rejection value. Returns a new promise for the transformed result.
188
189The `then()` method registers new fulfilled, rejection and progress handlers
190with a promise (all parameters are optional):
191
192  * `$onFulfilled` will be invoked once the promise is fulfilled and passed
193    the result as the first argument.
194  * `$onRejected` will be invoked once the promise is rejected and passed the
195    reason as the first argument.
196  * `$onProgress` (deprecated) will be invoked whenever the producer of the promise
197    triggers progress notifications and passed a single argument (whatever it
198    wants) to indicate progress.
199
200It returns a new promise that will fulfill with the return value of either
201`$onFulfilled` or `$onRejected`, whichever is called, or will reject with
202the thrown exception if either throws.
203
204A promise makes the following guarantees about handlers registered in
205the same call to `then()`:
206
207  1. Only one of `$onFulfilled` or `$onRejected` will be called,
208     never both.
209  2. `$onFulfilled` and `$onRejected` will never be called more
210     than once.
211  3. `$onProgress` (deprecated) may be called multiple times.
212
213#### See also
214
215* [resolve()](#resolve) - Creating a resolved promise
216* [reject()](#reject) - Creating a rejected promise
217* [ExtendedPromiseInterface::done()](#extendedpromiseinterfacedone)
218* [done() vs. then()](#done-vs-then)
219
220### ExtendedPromiseInterface
221
222The ExtendedPromiseInterface extends the PromiseInterface with useful shortcut
223and utility methods which are not part of the Promises/A specification.
224
225#### Implementations
226
227* [Promise](#promise-1)
228* [FulfilledPromise](#fulfilledpromise) (deprecated)
229* [RejectedPromise](#rejectedpromise) (deprecated)
230* [LazyPromise](#lazypromise) (deprecated)
231
232#### ExtendedPromiseInterface::done()
233
234```php
235$promise->done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null);
236```
237
238Consumes the promise's ultimate value if the promise fulfills, or handles the
239ultimate error.
240
241It will cause a fatal error if either `$onFulfilled` or `$onRejected` throw or
242return a rejected promise.
243
244Since the purpose of `done()` is consumption rather than transformation,
245`done()` always returns `null`.
246
247#### See also
248
249* [PromiseInterface::then()](#promiseinterfacethen)
250* [done() vs. then()](#done-vs-then)
251
252#### ExtendedPromiseInterface::otherwise()
253
254```php
255$promise->otherwise(callable $onRejected);
256```
257
258Registers a rejection handler for promise. It is a shortcut for:
259
260```php
261$promise->then(null, $onRejected);
262```
263
264Additionally, you can type hint the `$reason` argument of `$onRejected` to catch
265only specific errors.
266
267```php
268$promise
269    ->otherwise(function (\RuntimeException $reason) {
270        // Only catch \RuntimeException instances
271        // All other types of errors will propagate automatically
272    })
273    ->otherwise(function ($reason) {
274        // Catch other errors
275    )};
276```
277
278#### ExtendedPromiseInterface::always()
279
280```php
281$newPromise = $promise->always(callable $onFulfilledOrRejected);
282```
283
284Allows you to execute "cleanup" type tasks in a promise chain.
285
286It arranges for `$onFulfilledOrRejected` to be called, with no arguments,
287when the promise is either fulfilled or rejected.
288
289* If `$promise` fulfills, and `$onFulfilledOrRejected` returns successfully,
290  `$newPromise` will fulfill with the same value as `$promise`.
291* If `$promise` fulfills, and `$onFulfilledOrRejected` throws or returns a
292  rejected promise, `$newPromise` will reject with the thrown exception or
293  rejected promise's reason.
294* If `$promise` rejects, and `$onFulfilledOrRejected` returns successfully,
295  `$newPromise` will reject with the same reason as `$promise`.
296* If `$promise` rejects, and `$onFulfilledOrRejected` throws or returns a
297  rejected promise, `$newPromise` will reject with the thrown exception or
298  rejected promise's reason.
299
300`always()` behaves similarly to the synchronous finally statement. When combined
301with `otherwise()`, `always()` allows you to write code that is similar to the familiar
302synchronous catch/finally pair.
303
304Consider the following synchronous code:
305
306```php
307try {
308  return doSomething();
309} catch(\Exception $e) {
310    return handleError($e);
311} finally {
312    cleanup();
313}
314```
315
316Similar asynchronous code (with `doSomething()` that returns a promise) can be
317written:
318
319```php
320return doSomething()
321    ->otherwise('handleError')
322    ->always('cleanup');
323```
324
325#### ExtendedPromiseInterface::progress()
326
327> Deprecated in v2.6.0: Progress support is deprecated and should not be used anymore.
328
329```php
330$promise->progress(callable $onProgress);
331```
332
333Registers a handler for progress updates from promise. It is a shortcut for:
334
335```php
336$promise->then(null, null, $onProgress);
337```
338
339### CancellablePromiseInterface
340
341A cancellable promise provides a mechanism for consumers to notify the creator
342of the promise that they are not longer interested in the result of an
343operation.
344
345#### CancellablePromiseInterface::cancel()
346
347``` php
348$promise->cancel();
349```
350
351The `cancel()` method notifies the creator of the promise that there is no
352further interest in the results of the operation.
353
354Once a promise is settled (either fulfilled or rejected), calling `cancel()` on
355a promise has no effect.
356
357#### Implementations
358
359* [Promise](#promise-1)
360* [FulfilledPromise](#fulfilledpromise) (deprecated)
361* [RejectedPromise](#rejectedpromise) (deprecated)
362* [LazyPromise](#lazypromise) (deprecated)
363
364### Promise
365
366Creates a promise whose state is controlled by the functions passed to
367`$resolver`.
368
369```php
370$resolver = function (callable $resolve, callable $reject, callable $notify) {
371    // Do some work, possibly asynchronously, and then
372    // resolve or reject. You can notify of progress events (deprecated)
373    // along the way if you want/need.
374
375    $resolve($awesomeResult);
376    // or throw new Exception('Promise rejected');
377    // or $resolve($anotherPromise);
378    // or $reject($nastyError);
379    // or $notify($progressNotification);
380};
381
382$canceller = function () {
383    // Cancel/abort any running operations like network connections, streams etc.
384
385    // Reject promise by throwing an exception
386    throw new Exception('Promise cancelled');
387};
388
389$promise = new React\Promise\Promise($resolver, $canceller);
390```
391
392The promise constructor receives a resolver function and an optional canceller
393function which both will be called with 3 arguments:
394
395  * `$resolve($value)` - Primary function that seals the fate of the
396    returned promise. Accepts either a non-promise value, or another promise.
397    When called with a non-promise value, fulfills promise with that value.
398    When called with another promise, e.g. `$resolve($otherPromise)`, promise's
399    fate will be equivalent to that of `$otherPromise`.
400  * `$reject($reason)` - Function that rejects the promise. It is recommended to
401    just throw an exception instead of using `$reject()`.
402  * `$notify($update)` - Deprecated function that issues progress events for the promise.
403
404If the resolver or canceller throw an exception, the promise will be rejected
405with that thrown exception as the rejection reason.
406
407The resolver function will be called immediately, the canceller function only
408once all consumers called the `cancel()` method of the promise.
409
410### FulfilledPromise
411
412> Deprecated in v2.8.0: External usage of `FulfilledPromise` is deprecated, use `resolve()` instead.
413
414Creates a already fulfilled promise.
415
416```php
417$promise = React\Promise\FulfilledPromise($value);
418```
419
420Note, that `$value` **cannot** be a promise. It's recommended to use
421[resolve()](#resolve) for creating resolved promises.
422
423### RejectedPromise
424
425> Deprecated in v2.8.0: External usage of `RejectedPromise` is deprecated, use `reject()` instead.
426
427Creates a already rejected promise.
428
429```php
430$promise = React\Promise\RejectedPromise($reason);
431```
432
433Note, that `$reason` **cannot** be a promise. It's recommended to use
434[reject()](#reject) for creating rejected promises.
435
436### LazyPromise
437
438> Deprecated in v2.8.0: LazyPromise is deprecated and should not be used anymore.
439
440Creates a promise which will be lazily initialized by `$factory` once a consumer
441calls the `then()` method.
442
443```php
444$factory = function () {
445    $deferred = new React\Promise\Deferred();
446
447    // Do some heavy stuff here and resolve the deferred once completed
448
449    return $deferred->promise();
450};
451
452$promise = new React\Promise\LazyPromise($factory);
453
454// $factory will only be executed once we call then()
455$promise->then(function ($value) {
456});
457```
458
459### Functions
460
461Useful functions for creating, joining, mapping and reducing collections of
462promises.
463
464All functions working on promise collections (like `all()`, `race()`, `some()`
465etc.) support cancellation. This means, if you call `cancel()` on the returned
466promise, all promises in the collection are cancelled. If the collection itself
467is a promise which resolves to an array, this promise is also cancelled.
468
469#### resolve()
470
471```php
472$promise = React\Promise\resolve(mixed $promiseOrValue);
473```
474
475Creates a promise for the supplied `$promiseOrValue`.
476
477If `$promiseOrValue` is a value, it will be the resolution value of the
478returned promise.
479
480If `$promiseOrValue` is a thenable (any object that provides a `then()` method),
481a trusted promise that follows the state of the thenable is returned.
482
483If `$promiseOrValue` is a promise, it will be returned as is.
484
485Note: The promise returned is always a promise implementing
486[ExtendedPromiseInterface](#extendedpromiseinterface). If you pass in a custom
487promise which only implements [PromiseInterface](#promiseinterface), this
488promise will be assimilated to a extended promise following `$promiseOrValue`.
489
490#### reject()
491
492```php
493$promise = React\Promise\reject(mixed $promiseOrValue);
494```
495
496Creates a rejected promise for the supplied `$promiseOrValue`.
497
498If `$promiseOrValue` is a value, it will be the rejection value of the
499returned promise.
500
501If `$promiseOrValue` is a promise, its completion value will be the rejected
502value of the returned promise.
503
504This can be useful in situations where you need to reject a promise without
505throwing an exception. For example, it allows you to propagate a rejection with
506the value of another promise.
507
508#### all()
509
510```php
511$promise = React\Promise\all(array|React\Promise\PromiseInterface $promisesOrValues);
512```
513
514Returns a promise that will resolve only once all the items in
515`$promisesOrValues` have resolved. The resolution value of the returned promise
516will be an array containing the resolution values of each of the items in
517`$promisesOrValues`.
518
519#### race()
520
521```php
522$promise = React\Promise\race(array|React\Promise\PromiseInterface $promisesOrValues);
523```
524
525Initiates a competitive race that allows one winner. Returns a promise which is
526resolved in the same way the first settled promise resolves.
527
528#### any()
529
530```php
531$promise = React\Promise\any(array|React\Promise\PromiseInterface $promisesOrValues);
532```
533
534Returns a promise that will resolve when any one of the items in
535`$promisesOrValues` resolves. The resolution value of the returned promise
536will be the resolution value of the triggering item.
537
538The returned promise will only reject if *all* items in `$promisesOrValues` are
539rejected. The rejection value will be an array of all rejection reasons.
540
541The returned promise will also reject with a `React\Promise\Exception\LengthException`
542if `$promisesOrValues` contains 0 items.
543
544#### some()
545
546```php
547$promise = React\Promise\some(array|React\Promise\PromiseInterface $promisesOrValues, integer $howMany);
548```
549
550Returns a promise that will resolve when `$howMany` of the supplied items in
551`$promisesOrValues` resolve. The resolution value of the returned promise
552will be an array of length `$howMany` containing the resolution values of the
553triggering items.
554
555The returned promise will reject if it becomes impossible for `$howMany` items
556to resolve (that is, when `(count($promisesOrValues) - $howMany) + 1` items
557reject). The rejection value will be an array of
558`(count($promisesOrValues) - $howMany) + 1` rejection reasons.
559
560The returned promise will also reject with a `React\Promise\Exception\LengthException`
561if `$promisesOrValues` contains less items than `$howMany`.
562
563#### map()
564
565```php
566$promise = React\Promise\map(array|React\Promise\PromiseInterface $promisesOrValues, callable $mapFunc);
567```
568
569Traditional map function, similar to `array_map()`, but allows input to contain
570promises and/or values, and `$mapFunc` may return either a value or a promise.
571
572The map function receives each item as argument, where item is a fully resolved
573value of a promise or value in `$promisesOrValues`.
574
575#### reduce()
576
577```php
578$promise = React\Promise\reduce(array|React\Promise\PromiseInterface $promisesOrValues, callable $reduceFunc , $initialValue = null);
579```
580
581Traditional reduce function, similar to `array_reduce()`, but input may contain
582promises and/or values, and `$reduceFunc` may return either a value or a
583promise, *and* `$initialValue` may be a promise or a value for the starting
584value.
585
586### PromisorInterface
587
588The `React\Promise\PromisorInterface` provides a common interface for objects
589that provide a promise. `React\Promise\Deferred` implements it, but since it
590is part of the public API anyone can implement it.
591
592Examples
593--------
594
595### How to use Deferred
596
597```php
598function getAwesomeResultPromise()
599{
600    $deferred = new React\Promise\Deferred();
601
602    // Execute a Node.js-style function using the callback pattern
603    computeAwesomeResultAsynchronously(function ($error, $result) use ($deferred) {
604        if ($error) {
605            $deferred->reject($error);
606        } else {
607            $deferred->resolve($result);
608        }
609    });
610
611    // Return the promise
612    return $deferred->promise();
613}
614
615getAwesomeResultPromise()
616    ->then(
617        function ($value) {
618            // Deferred resolved, do something with $value
619        },
620        function ($reason) {
621            // Deferred rejected, do something with $reason
622        },
623        function ($update) {
624            // Progress notification triggered, do something with $update
625        }
626    );
627```
628
629### How promise forwarding works
630
631A few simple examples to show how the mechanics of Promises/A forwarding works.
632These examples are contrived, of course, and in real usage, promise chains will
633typically be spread across several function calls, or even several levels of
634your application architecture.
635
636#### Resolution forwarding
637
638Resolved promises forward resolution values to the next promise.
639The first promise, `$deferred->promise()`, will resolve with the value passed
640to `$deferred->resolve()` below.
641
642Each call to `then()` returns a new promise that will resolve with the return
643value of the previous handler. This creates a promise "pipeline".
644
645```php
646$deferred = new React\Promise\Deferred();
647
648$deferred->promise()
649    ->then(function ($x) {
650        // $x will be the value passed to $deferred->resolve() below
651        // and returns a *new promise* for $x + 1
652        return $x + 1;
653    })
654    ->then(function ($x) {
655        // $x === 2
656        // This handler receives the return value of the
657        // previous handler.
658        return $x + 1;
659    })
660    ->then(function ($x) {
661        // $x === 3
662        // This handler receives the return value of the
663        // previous handler.
664        return $x + 1;
665    })
666    ->then(function ($x) {
667        // $x === 4
668        // This handler receives the return value of the
669        // previous handler.
670        echo 'Resolve ' . $x;
671    });
672
673$deferred->resolve(1); // Prints "Resolve 4"
674```
675
676#### Rejection forwarding
677
678Rejected promises behave similarly, and also work similarly to try/catch:
679When you catch an exception, you must rethrow for it to propagate.
680
681Similarly, when you handle a rejected promise, to propagate the rejection,
682"rethrow" it by either returning a rejected promise, or actually throwing
683(since promise translates thrown exceptions into rejections)
684
685```php
686$deferred = new React\Promise\Deferred();
687
688$deferred->promise()
689    ->then(function ($x) {
690        throw new \Exception($x + 1);
691    })
692    ->otherwise(function (\Exception $x) {
693        // Propagate the rejection
694        throw $x;
695    })
696    ->otherwise(function (\Exception $x) {
697        // Can also propagate by returning another rejection
698        return React\Promise\reject(
699            new \Exception($x->getMessage() + 1)
700        );
701    })
702    ->otherwise(function ($x) {
703        echo 'Reject ' . $x->getMessage(); // 3
704    });
705
706$deferred->resolve(1);  // Prints "Reject 3"
707```
708
709#### Mixed resolution and rejection forwarding
710
711Just like try/catch, you can choose to propagate or not. Mixing resolutions and
712rejections will still forward handler results in a predictable way.
713
714```php
715$deferred = new React\Promise\Deferred();
716
717$deferred->promise()
718    ->then(function ($x) {
719        return $x + 1;
720    })
721    ->then(function ($x) {
722        throw new \Exception($x + 1);
723    })
724    ->otherwise(function (\Exception $x) {
725        // Handle the rejection, and don't propagate.
726        // This is like catch without a rethrow
727        return $x->getMessage() + 1;
728    })
729    ->then(function ($x) {
730        echo 'Mixed ' . $x; // 4
731    });
732
733$deferred->resolve(1);  // Prints "Mixed 4"
734```
735
736#### Progress event forwarding
737
738> Deprecated in v2.6.0: Progress support is deprecated and should not be used anymore.
739
740In the same way as resolution and rejection handlers, your progress handler
741**MUST** return a progress event to be propagated to the next link in the chain.
742If you return nothing, `null` will be propagated.
743
744Also in the same way as resolutions and rejections, if you don't register a
745progress handler, the update will be propagated through.
746
747If your progress handler throws an exception, the exception will be propagated
748to the next link in the chain. The best thing to do is to ensure your progress
749handlers do not throw exceptions.
750
751This gives you the opportunity to transform progress events at each step in the
752chain so that they are meaningful to the next step. It also allows you to choose
753not to transform them, and simply let them propagate untransformed, by not
754registering a progress handler.
755
756```php
757$deferred = new React\Promise\Deferred();
758
759$deferred->promise()
760    ->progress(function ($update) {
761        return $update + 1;
762    })
763    ->progress(function ($update) {
764        echo 'Progress ' . $update; // 2
765    });
766
767$deferred->notify(1);  // Prints "Progress 2"
768```
769
770### done() vs. then()
771
772The golden rule is:
773
774    Either return your promise, or call done() on it.
775
776At a first glance, `then()` and `done()` seem very similar. However, there are
777important distinctions.
778
779The intent of `then()` is to transform a promise's value and to pass or return
780a new promise for the transformed value along to other parts of your code.
781
782The intent of `done()` is to consume a promise's value, transferring
783responsibility for the value to your code.
784
785In addition to transforming a value, `then()` allows you to recover from, or
786propagate intermediate errors. Any errors that are not handled will be caught
787by the promise machinery and used to reject the promise returned by `then()`.
788
789Calling `done()` transfers all responsibility for errors to your code. If an
790error (either a thrown exception or returned rejection) escapes the
791`$onFulfilled` or `$onRejected` callbacks you provide to done, it will be
792rethrown in an uncatchable way causing a fatal error.
793
794```php
795function getJsonResult()
796{
797    return queryApi()
798        ->then(
799            // Transform API results to an object
800            function ($jsonResultString) {
801                return json_decode($jsonResultString);
802            },
803            // Transform API errors to an exception
804            function ($jsonErrorString) {
805                $object = json_decode($jsonErrorString);
806                throw new ApiErrorException($object->errorMessage);
807            }
808        );
809}
810
811// Here we provide no rejection handler. If the promise returned has been
812// rejected, the ApiErrorException will be thrown
813getJsonResult()
814    ->done(
815        // Consume transformed object
816        function ($jsonResultObject) {
817            // Do something with $jsonResultObject
818        }
819    );
820
821// Here we provide a rejection handler which will either throw while debugging
822// or log the exception
823getJsonResult()
824    ->done(
825        function ($jsonResultObject) {
826            // Do something with $jsonResultObject
827        },
828        function (ApiErrorException $exception) {
829            if (isDebug()) {
830                throw $exception;
831            } else {
832                logException($exception);
833            }
834        }
835    );
836```
837
838Note that if a rejection value is not an instance of `\Exception`, it will be
839wrapped in an exception of the type `React\Promise\UnhandledRejectionException`.
840
841You can get the original rejection reason by calling `$exception->getReason()`.
842
843Install
844-------
845
846The recommended way to install this library is [through Composer](https://getcomposer.org).
847[New to Composer?](https://getcomposer.org/doc/00-intro.md)
848
849This project follows [SemVer](https://semver.org/).
850This will install the latest supported version:
851
852```bash
853composer require react/promise:^2.11
854```
855
856See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.
857
858This project aims to run on any platform and thus does not require any PHP
859extensions and supports running on legacy PHP 5.4 through current PHP 8+ and HHVM.
860It's *highly recommended to use the latest supported PHP version* for this project.
861
862Credits
863-------
864
865Promise is a port of [when.js](https://github.com/cujojs/when)
866by [Brian Cavalier](https://github.com/briancavalier).
867
868Also, large parts of the documentation have been ported from the when.js
869[Wiki](https://github.com/cujojs/when/wiki) and the
870[API docs](https://github.com/cujojs/when/blob/master/docs/api.md).
871
872License
873-------
874
875Released under the [MIT](LICENSE) license.
876