11

I am attempting to use guzzle promises in order to make some http calls, to illustrate what I have, I have made this simple example where a fake http request would take 5 seconds:

$then = microtime(true);

$promise = new Promise(
    function() use (&$promise) {
        //Make a request to an http server
        $httpResponse = 200;
        sleep(5);
        $promise->resolve($httpResponse);
    });

$promise2 = new Promise(
    function() use (&$promise2) {
        //Make a request to an http server
        $httpResponse = 200;
        sleep(5);
        $promise2->resolve($httpResponse);
    });

echo 'PROMISE_1 ' . $promise->wait();
echo 'PROMISE_2 ' . $promise2->wait();

echo 'Took: ' . (microtime(true) - $then);

Now what I would want to do is start both of them, and then make both echo's await for the response. What actually happens is promise 1 fires, waits 5 seconds then fires promise 2 and waits another 5 seconds.

From my understanding I should maybe be using the ->resolve(); function of a promise to make it start, but I dont know how to pass resolve a function in which I would make an http call

Rich
  • 173
  • 1
  • 2
  • 8

3 Answers3

6

By using wait() you're forcing the promise to be resolved synchronously: https://github.com/guzzle/promises#synchronous-wait

According to the Guzzle FAQ you should use requestAsync() with your RESTful calls:

Can Guzzle send asynchronous requests?

Yes. You can use the requestAsync, sendAsync, getAsync, headAsync, putAsync, postAsync, deleteAsync, and patchAsync methods of a client to send an asynchronous request. The client will return a GuzzleHttp\Promise\PromiseInterface object. You can chain then functions off of the promise.

$promise = $client->requestAsync('GET', 'http://httpbin.org/get');
$promise->then(function ($response) {
echo 'Got a response! ' . $response->getStatusCode(); });

You can force an asynchronous response to complete using the wait() method of the returned promise.

$promise = $client->requestAsync('GET', 'http://httpbin.org/get');
$response = $promise->wait();
Digital Chris
  • 6,177
  • 1
  • 20
  • 29
  • The main problem is the http call is made various function calls down the rabbit hole. Ideally I want to be able to wrap my `doHttpCallAndReturn();` in a promise, and fetch the response when ready. – Rich May 23 '16 at 15:54
  • 2
    Has this not just restated the question without answering it? I think the original question is asking how to wait for multiple promises, all fired off asynchronously, and so expected to possibly finish in any order. – Jason Jan 02 '18 at 14:24
  • @Rich have you figured this out? – The Onin Jan 09 '18 at 04:11
  • Using .then() won't do both calls in parallel, but one after another. In practice you usually want to send out multiple requests, wait for all to be finished, and do something. – Velizar Hristov Nov 23 '18 at 15:15
6

This question is a little old but I see no answer, so I'll give it a shot, maybe someone will find it helpful.

You can use the function all($promises).

I can't find documentation about this function but you can find its implementation here.

The comment above this function starts like this:

Given an array of promises, return a promise that is fulfilled when all the items in the array are fulfilled.

Sounds like what you are looking for, so you can do something like this:

$then = microtime(true);
$promises = [];

$promises[] = new Promise(
    function() use (&$promise) {
        //Make a request to an http server
        $httpResponse = 200;
        sleep(5);
        $promise->resolve($httpResponse);
    });

$promises[] = new Promise(
    function() use (&$promise2) {
        //Make a request to an http server
        $httpResponse = 200;
        sleep(5);
        $promise2->resolve($httpResponse);
    });

all($promises)->wait();
echo 'Took: ' . (microtime(true) - $then);

If this function isn't the one that helps you solve your problem, there are other interesting functions in that file like some($count, $promises), any($promises) or settle($promises).

vimuzumu
  • 77
  • 1
  • 1
  • Sleep is still going to halt the program for 5 seconds each. – Evert Dec 04 '18 at 16:54
  • it seems to work, the question is why. i would guess it terminates after the error message: `Call to a member function resolve() on null` – Nikola Petkanski Feb 20 '19 at 15:45
  • Nope, the given code example is not working: "Fatal error: Uncaught Error: Call to a member function resolve() on null" – Andrew Jul 08 '21 at 06:09
1

You can use the function Utils::all($promises)->wait();

Here is code example for "guzzlehttp/promises": "^1.4"

$promises = [];
$key = 0;
foreach(something...) {
    $key++;
    $promises[$key] = new Promise(
        function() use (&$promises, $key) {
            // here you can call some sort of async operation
            // ...
            // at the end call ->resolve method
            $promises[$key]->resolve('bingo');
        }
    );      
}

$res = Utils::all($promises)->wait();

It is important that your operation in promise must be non-blocking if you want to get concurrent workflow. For example, sleep(1) is blocking operation. So, 10 promises with sleep(1) - together are going to wait 10 sec anyway.

ashestakov
  • 11
  • 1