1Promise
2=======
3
4A lightweight implementation of
5[CommonJS Promises/A](http://wiki.commonjs.org/wiki/Promises/A) for PHP.
6
7[![Build Status](https://travis-ci.org/reactphp/promise.svg?branch=master)](http://travis-ci.org/reactphp/promise)
8[![Coverage Status](https://coveralls.io/repos/github/reactphp/promise/badge.svg?branch=master)](https://coveralls.io/github/reactphp/promise?branch=master)
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)
177* [RejectedPromise](#rejectedpromise)
178* [LazyPromise](#lazypromise)
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)
229* [RejectedPromise](#rejectedpromise)
230* [LazyPromise](#lazypromise)
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)
361* [RejectedPromise](#rejectedpromise)
362* [LazyPromise](#lazypromise)
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
412Creates a already fulfilled promise.
413
414```php
415$promise = React\Promise\FulfilledPromise($value);
416```
417
418Note, that `$value` **cannot** be a promise. It's recommended to use
419[resolve()](#resolve) for creating resolved promises.
420
421### RejectedPromise
422
423Creates a already rejected promise.
424
425```php
426$promise = React\Promise\RejectedPromise($reason);
427```
428
429Note, that `$reason` **cannot** be a promise. It's recommended to use
430[reject()](#reject) for creating rejected promises.
431
432### LazyPromise
433
434> Deprecated in v2.8.0: LazyPromise is deprecated and should not be used anymore.
435
436Creates a promise which will be lazily initialized by `$factory` once a consumer
437calls the `then()` method.
438
439```php
440$factory = function () {
441    $deferred = new React\Promise\Deferred();
442
443    // Do some heavy stuff here and resolve the deferred once completed
444
445    return $deferred->promise();
446};
447
448$promise = new React\Promise\LazyPromise($factory);
449
450// $factory will only be executed once we call then()
451$promise->then(function ($value) {
452});
453```
454
455### Functions
456
457Useful functions for creating, joining, mapping and reducing collections of
458promises.
459
460All functions working on promise collections (like `all()`, `race()`, `some()`
461etc.) support cancellation. This means, if you call `cancel()` on the returned
462promise, all promises in the collection are cancelled. If the collection itself
463is a promise which resolves to an array, this promise is also cancelled.
464
465#### resolve()
466
467```php
468$promise = React\Promise\resolve(mixed $promiseOrValue);
469```
470
471Creates a promise for the supplied `$promiseOrValue`.
472
473If `$promiseOrValue` is a value, it will be the resolution value of the
474returned promise.
475
476If `$promiseOrValue` is a thenable (any object that provides a `then()` method),
477a trusted promise that follows the state of the thenable is returned.
478
479If `$promiseOrValue` is a promise, it will be returned as is.
480
481Note: The promise returned is always a promise implementing
482[ExtendedPromiseInterface](#extendedpromiseinterface). If you pass in a custom
483promise which only implements [PromiseInterface](#promiseinterface), this
484promise will be assimilated to a extended promise following `$promiseOrValue`.
485
486#### reject()
487
488```php
489$promise = React\Promise\reject(mixed $promiseOrValue);
490```
491
492Creates a rejected promise for the supplied `$promiseOrValue`.
493
494If `$promiseOrValue` is a value, it will be the rejection value of the
495returned promise.
496
497If `$promiseOrValue` is a promise, its completion value will be the rejected
498value of the returned promise.
499
500This can be useful in situations where you need to reject a promise without
501throwing an exception. For example, it allows you to propagate a rejection with
502the value of another promise.
503
504#### all()
505
506```php
507$promise = React\Promise\all(array|React\Promise\PromiseInterface $promisesOrValues);
508```
509
510Returns a promise that will resolve only once all the items in
511`$promisesOrValues` have resolved. The resolution value of the returned promise
512will be an array containing the resolution values of each of the items in
513`$promisesOrValues`.
514
515#### race()
516
517```php
518$promise = React\Promise\race(array|React\Promise\PromiseInterface $promisesOrValues);
519```
520
521Initiates a competitive race that allows one winner. Returns a promise which is
522resolved in the same way the first settled promise resolves.
523
524#### any()
525
526```php
527$promise = React\Promise\any(array|React\Promise\PromiseInterface $promisesOrValues);
528```
529
530Returns a promise that will resolve when any one of the items in
531`$promisesOrValues` resolves. The resolution value of the returned promise
532will be the resolution value of the triggering item.
533
534The returned promise will only reject if *all* items in `$promisesOrValues` are
535rejected. The rejection value will be an array of all rejection reasons.
536
537The returned promise will also reject with a `React\Promise\Exception\LengthException`
538if `$promisesOrValues` contains 0 items.
539
540#### some()
541
542```php
543$promise = React\Promise\some(array|React\Promise\PromiseInterface $promisesOrValues, integer $howMany);
544```
545
546Returns a promise that will resolve when `$howMany` of the supplied items in
547`$promisesOrValues` resolve. The resolution value of the returned promise
548will be an array of length `$howMany` containing the resolution values of the
549triggering items.
550
551The returned promise will reject if it becomes impossible for `$howMany` items
552to resolve (that is, when `(count($promisesOrValues) - $howMany) + 1` items
553reject). The rejection value will be an array of
554`(count($promisesOrValues) - $howMany) + 1` rejection reasons.
555
556The returned promise will also reject with a `React\Promise\Exception\LengthException`
557if `$promisesOrValues` contains less items than `$howMany`.
558
559#### map()
560
561```php
562$promise = React\Promise\map(array|React\Promise\PromiseInterface $promisesOrValues, callable $mapFunc);
563```
564
565Traditional map function, similar to `array_map()`, but allows input to contain
566promises and/or values, and `$mapFunc` may return either a value or a promise.
567
568The map function receives each item as argument, where item is a fully resolved
569value of a promise or value in `$promisesOrValues`.
570
571#### reduce()
572
573```php
574$promise = React\Promise\reduce(array|React\Promise\PromiseInterface $promisesOrValues, callable $reduceFunc , $initialValue = null);
575```
576
577Traditional reduce function, similar to `array_reduce()`, but input may contain
578promises and/or values, and `$reduceFunc` may return either a value or a
579promise, *and* `$initialValue` may be a promise or a value for the starting
580value.
581
582### PromisorInterface
583
584The `React\Promise\PromisorInterface` provides a common interface for objects
585that provide a promise. `React\Promise\Deferred` implements it, but since it
586is part of the public API anyone can implement it.
587
588Examples
589--------
590
591### How to use Deferred
592
593```php
594function getAwesomeResultPromise()
595{
596    $deferred = new React\Promise\Deferred();
597
598    // Execute a Node.js-style function using the callback pattern
599    computeAwesomeResultAsynchronously(function ($error, $result) use ($deferred) {
600        if ($error) {
601            $deferred->reject($error);
602        } else {
603            $deferred->resolve($result);
604        }
605    });
606
607    // Return the promise
608    return $deferred->promise();
609}
610
611getAwesomeResultPromise()
612    ->then(
613        function ($value) {
614            // Deferred resolved, do something with $value
615        },
616        function ($reason) {
617            // Deferred rejected, do something with $reason
618        },
619        function ($update) {
620            // Progress notification triggered, do something with $update
621        }
622    );
623```
624
625### How promise forwarding works
626
627A few simple examples to show how the mechanics of Promises/A forwarding works.
628These examples are contrived, of course, and in real usage, promise chains will
629typically be spread across several function calls, or even several levels of
630your application architecture.
631
632#### Resolution forwarding
633
634Resolved promises forward resolution values to the next promise.
635The first promise, `$deferred->promise()`, will resolve with the value passed
636to `$deferred->resolve()` below.
637
638Each call to `then()` returns a new promise that will resolve with the return
639value of the previous handler. This creates a promise "pipeline".
640
641```php
642$deferred = new React\Promise\Deferred();
643
644$deferred->promise()
645    ->then(function ($x) {
646        // $x will be the value passed to $deferred->resolve() below
647        // and returns a *new promise* for $x + 1
648        return $x + 1;
649    })
650    ->then(function ($x) {
651        // $x === 2
652        // This handler receives the return value of the
653        // previous handler.
654        return $x + 1;
655    })
656    ->then(function ($x) {
657        // $x === 3
658        // This handler receives the return value of the
659        // previous handler.
660        return $x + 1;
661    })
662    ->then(function ($x) {
663        // $x === 4
664        // This handler receives the return value of the
665        // previous handler.
666        echo 'Resolve ' . $x;
667    });
668
669$deferred->resolve(1); // Prints "Resolve 4"
670```
671
672#### Rejection forwarding
673
674Rejected promises behave similarly, and also work similarly to try/catch:
675When you catch an exception, you must rethrow for it to propagate.
676
677Similarly, when you handle a rejected promise, to propagate the rejection,
678"rethrow" it by either returning a rejected promise, or actually throwing
679(since promise translates thrown exceptions into rejections)
680
681```php
682$deferred = new React\Promise\Deferred();
683
684$deferred->promise()
685    ->then(function ($x) {
686        throw new \Exception($x + 1);
687    })
688    ->otherwise(function (\Exception $x) {
689        // Propagate the rejection
690        throw $x;
691    })
692    ->otherwise(function (\Exception $x) {
693        // Can also propagate by returning another rejection
694        return React\Promise\reject(
695            new \Exception($x->getMessage() + 1)
696        );
697    })
698    ->otherwise(function ($x) {
699        echo 'Reject ' . $x->getMessage(); // 3
700    });
701
702$deferred->resolve(1);  // Prints "Reject 3"
703```
704
705#### Mixed resolution and rejection forwarding
706
707Just like try/catch, you can choose to propagate or not. Mixing resolutions and
708rejections will still forward handler results in a predictable way.
709
710```php
711$deferred = new React\Promise\Deferred();
712
713$deferred->promise()
714    ->then(function ($x) {
715        return $x + 1;
716    })
717    ->then(function ($x) {
718        throw new \Exception($x + 1);
719    })
720    ->otherwise(function (\Exception $x) {
721        // Handle the rejection, and don't propagate.
722        // This is like catch without a rethrow
723        return $x->getMessage() + 1;
724    })
725    ->then(function ($x) {
726        echo 'Mixed ' . $x; // 4
727    });
728
729$deferred->resolve(1);  // Prints "Mixed 4"
730```
731
732#### Progress event forwarding
733
734> Deprecated in v2.6.0: Progress support is deprecated and should not be used anymore.
735
736In the same way as resolution and rejection handlers, your progress handler
737**MUST** return a progress event to be propagated to the next link in the chain.
738If you return nothing, `null` will be propagated.
739
740Also in the same way as resolutions and rejections, if you don't register a
741progress handler, the update will be propagated through.
742
743If your progress handler throws an exception, the exception will be propagated
744to the next link in the chain. The best thing to do is to ensure your progress
745handlers do not throw exceptions.
746
747This gives you the opportunity to transform progress events at each step in the
748chain so that they are meaningful to the next step. It also allows you to choose
749not to transform them, and simply let them propagate untransformed, by not
750registering a progress handler.
751
752```php
753$deferred = new React\Promise\Deferred();
754
755$deferred->promise()
756    ->progress(function ($update) {
757        return $update + 1;
758    })
759    ->progress(function ($update) {
760        echo 'Progress ' . $update; // 2
761    });
762
763$deferred->notify(1);  // Prints "Progress 2"
764```
765
766### done() vs. then()
767
768The golden rule is:
769
770    Either return your promise, or call done() on it.
771
772At a first glance, `then()` and `done()` seem very similar. However, there are
773important distinctions.
774
775The intent of `then()` is to transform a promise's value and to pass or return
776a new promise for the transformed value along to other parts of your code.
777
778The intent of `done()` is to consume a promise's value, transferring
779responsibility for the value to your code.
780
781In addition to transforming a value, `then()` allows you to recover from, or
782propagate intermediate errors. Any errors that are not handled will be caught
783by the promise machinery and used to reject the promise returned by `then()`.
784
785Calling `done()` transfers all responsibility for errors to your code. If an
786error (either a thrown exception or returned rejection) escapes the
787`$onFulfilled` or `$onRejected` callbacks you provide to done, it will be
788rethrown in an uncatchable way causing a fatal error.
789
790```php
791function getJsonResult()
792{
793    return queryApi()
794        ->then(
795            // Transform API results to an object
796            function ($jsonResultString) {
797                return json_decode($jsonResultString);
798            },
799            // Transform API errors to an exception
800            function ($jsonErrorString) {
801                $object = json_decode($jsonErrorString);
802                throw new ApiErrorException($object->errorMessage);
803            }
804        );
805}
806
807// Here we provide no rejection handler. If the promise returned has been
808// rejected, the ApiErrorException will be thrown
809getJsonResult()
810    ->done(
811        // Consume transformed object
812        function ($jsonResultObject) {
813            // Do something with $jsonResultObject
814        }
815    );
816
817// Here we provide a rejection handler which will either throw while debugging
818// or log the exception
819getJsonResult()
820    ->done(
821        function ($jsonResultObject) {
822            // Do something with $jsonResultObject
823        },
824        function (ApiErrorException $exception) {
825            if (isDebug()) {
826                throw $exception;
827            } else {
828                logException($exception);
829            }
830        }
831    );
832```
833
834Note that if a rejection value is not an instance of `\Exception`, it will be
835wrapped in an exception of the type `React\Promise\UnhandledRejectionException`.
836
837You can get the original rejection reason by calling `$exception->getReason()`.
838
839Install
840-------
841
842The recommended way to install this library is [through Composer](https://getcomposer.org).
843[New to Composer?](https://getcomposer.org/doc/00-intro.md)
844
845This project follows [SemVer](https://semver.org/).
846This will install the latest supported version:
847
848```bash
849$ composer require react/promise:^2.7
850```
851
852See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.
853
854This project aims to run on any platform and thus does not require any PHP
855extensions and supports running on legacy PHP 5.4 through current PHP 7+ and HHVM.
856It's *highly recommended to use PHP 7+* for this project due to its vast
857performance improvements.
858
859Credits
860-------
861
862Promise is a port of [when.js](https://github.com/cujojs/when)
863by [Brian Cavalier](https://github.com/briancavalier).
864
865Also, large parts of the documentation have been ported from the when.js
866[Wiki](https://github.com/cujojs/when/wiki) and the
867[API docs](https://github.com/cujojs/when/blob/master/docs/api.md).
868
869License
870-------
871
872Released under the [MIT](LICENSE) license.
873