I'm doing POC implementation of async http requests in PHP. The symfony http client works well when we retrieve response data following way:
$response1 = $httpClient->request('GET', 'https://127.0.0.1:8000/service/a');
$response2 = $httpClient->request('GET', 'https://127.0.0.1:8000/service/b');
$array1 = $response1->toArray();
$array2 = $response2->toArray();
Considering both services A and B take 5 seconds to execute, our client code will have to wait only 5 seconds totally.
Now, the problem is that usually we write different repositories for different clients (RepositoryA, RepositoryB). Hence, in client code we won't operate on simple HttpResponseInterface object, but on some DTO instead.
I'd like to take advantage of async http requests, while retaining the repositories separation.
The simplified approach looks like this:
private function getResponse1(): Generator
{
$response1 = $this->httpClient->request('GET', 'https://127.0.0.1:8000/service/a');
return yield $response1->toArray();
}
private function getResponse2(): Generator
{
$response2 = $this->httpClient->request('GET', 'https://127.0.0.1:8000/service/b');
return yield $response2->toArray();
}
$response1 = $this->getResponse1();
$response2 = $this->getResponse2();
$array1 = $response1->current();
$array2 = $response2->current();
If you are familiar with JS, this current() call on Generator object is somewhat similar to await construct.
Finally, the question is why doesn't this approach work? Since measurements show that total time is 10 seconds of waiting with generators, while only 5 seconds without them.
Here's the Console command, which may be used to reproduce the issue: https://github.com/rela589n/generator-as-promise-poc/blob/master/src/Command/TestSyncSfClientGeneratorsCommand.php
Ans also there's a working example of code not using generators: https://github.com/rela589n/generator-as-promise-poc/blob/master/src/Command/TestAsyncSfHttpClientCommand.php
The problem is likely that the first yield of your generator is the total result:
(apart from that return yield is somewhat bogus now as I pasted it in...)
Now the generator that is also doing the resource acquistion:
right in front of the returning yield, it means that the generator will wait for this request to finish.
same for the second generator.
Therefore, when you distribute both requests in two different generators, you're loosing the ability to invoke them in parallel (as in after each other) as you already make - via the generator implementation - the generator waiting for the first yield.
Therefore, fake the first yield and work with the return construction.
This could probably work, however it remains still unclear to me what toArray() actually does as I don't known that library.
However, in regard to the PHP Generator implementation, it is that it always fully initializes, which is why it can't rewind and needs to acquire the first yield result.
Therefore the first yield must be the non-blocking result from the http-client-request protocol.