Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guzzle 6 - Promises - Catching Exceptions

I don't really understand how to catch an exception (forward it) within the onReject handler. I was wondering if anyone can point me in the right direction on how to successfully do so.

I'm sending in some async requests and when one fails with "An uncaught Exception was encountered - Type: GuzzleHttp\Exception\ClientException" it never gets caught.

I have read:

  • https://github.com/reactphp/promise#how-promise-forwarding-works
  • https://github.com/guzzle/promises

But it's not clear why the following doesn't work. My understanding is when the ClientException is thrown within the onReject (RequestException) it will then push it further down to the next onReject (ClientException) and be caught properly.

Any help would be appreciated.

$client = new GuzzleHttp\Client();

$promise = $client->requestAsync('POST', SOME_URL, [
  ... SOME_PARAMS ...
]);

$promise->then(
function (ResponseInterface $res) {
  //ok
},
function (RequestException $e) {
  //inside here throws a ClientException
}
)->then(null, function (ClientException $e) {
  //Why does it not get caught/forwarded to this error handler?
});
like image 703
Fermier Avatar asked Jun 05 '16 20:06

Fermier


Video Answer


2 Answers

According to the guzzle documentation,

If an exception is thrown in an $onRejected callback, subsequent $onRejected callbacks are invoked with the thrown exception as the reason.

So this should work:

$promise
->then(
    function (ResponseInterface $res) {
        // this will be called when the promise resolves
        return $someVal;
    },
    function (RequestException $e) {
        // this will be called when the promise resolving failed
        // if you want this to bubble down further the then-line, just re-throw:
        throw $e;
    }
)
->then(
    function ($someVal) {

    },
    function (RequestException $e) {
        // now the above thrown Exception should be passed in here
    });
like image 144
lkoell Avatar answered Oct 27 '22 05:10

lkoell


Guzzle Promises follow Promises/A+ standard. Therefore we can rely on the official description to grasp the behavior you are curious about:

2.2.7.1. If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).

2.2.7.2. If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.

And later for the 2.2.7.2 case:

2.3.2. If x is a promise, adopt its state

Therefore you could either follow the solution proposed by @lkoell or to return RejectedPromise from the callback which will force a subsequent promise to adopt the rejected state.

$promiseA = $promise
    ->then(
        function (ResponseInterface $res) {
          //ok
        },
        function (RequestException $e) {
          //This will force $promiseB adopt $promiseC state and get rejected
          return $promiseC = new RejectedPromise($clientException);
        }
);
$promiseB = $promiseA->then(null, function (ClientException $e) {
          // There you will get rejection
});

This way is more flexible as you can reject a promise not only with an exception but any reason (except a promise).

like image 43
origaminal Avatar answered Oct 27 '22 05:10

origaminal