我正在用 PHP 进行异步 http 请求的 POC 实现。当我们按照以下方式检索响应数据时,symfony http 客户端运行良好:
$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();
考虑到服务 A 和 B 都需要 5 秒来执行,我们的客户端代码总共只需等待 5 秒。
现在的问题是,通常我们会为不同的客户端编写不同的存储库(RepositoryA、RepositoryB)。因此,在客户端代码中,我们不会对简单的 HttpResponseInterface 对象进行操作,而是对某些 DTO 进行操作。
我想利用异步 http 请求,同时保留存储库分离。
简化的方法如下所示:
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();
如果您熟悉 JS,那么对
current()
对象的 Generator
调用有点类似于 await
构造。
最后的问题是,为什么这种方法行不通呢? 由于测量显示,使用发电机时总等待时间为 10 秒,而没有发电机时则只有 5 秒。
这是控制台命令,可用于重现该问题: https://github.com/rela589n/generator-as-promise-poc/blob/master/src/Command/TestSyncSfClientGeneratorsCommand.php
还有一个不使用生成器的代码的工作示例: https://github.com/rela589n/generator-as-promise-poc/blob/master/src/Command/TestAsyncSfHttpClientCommand.php
问题很可能是你的生成器的第一个产量就是总结果:
return yield $response1->toArray();
(除了回报率现在有点假,因为我将其粘贴到...)
现在生成器也在进行资源获取:
$response1 = $this->httpClient->request('GET', 'https://127.0.0.1:8000/service/a');
在返回的yield前面,这意味着生成器将等待该请求完成。
第二个发电机也一样。
因此,当您将两个请求分发到两个不同的生成器中时,您将失去并行调用它们的能力(如在彼此之后),就像您已经通过生成器实现所做的那样 - 生成器等待第一个收益。 因此,伪造第一个产量并进行返回构造。
private function getResponse2(): Generator
{
$response2 = $this->httpClient->request('GET', 'https://127.0.0.1:8000/service/b');
yield $response2; # first current() (implicit rewind()) stops here after invocation.
return yield $response2->toArray(); # second current() fetches the result
}
这可能会起作用,但是我仍然不清楚 toArray() 实际做了什么,因为我不知道该库。
但是,对于 PHP Generator 实现,它总是完全初始化,这就是它无法倒带的原因。
因此第一个yield一定是http-client-request协议的非阻塞结果。