【问题标题】:Using Guzzle promises asyncronously异步使用 Guzzle 承诺
【发布时间】:2021-10-06 05:12:52
【问题描述】:

我正在尝试使用 guzzle promises 来进行一些 http 调用,为了说明我所拥有的,我做了一个简单的示例,其中一个虚假的 http 请求需要 5 秒:

$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);

现在我想做的是启动它们,然后让两个 echo 等待响应。实际发生的是 promise 1 触发,等待 5 秒,然后触发 promise 2 并再等待 5 秒。

据我了解,我可能应该使用 promise 的 ->resolve(); 函数来启动它,但我不知道如何通过 resolve 一个函数来进行 http 调用

【问题讨论】:

    标签: php asynchronous promise guzzle


    【解决方案1】:

    通过使用wait(),您将强制同步解决承诺:https://github.com/guzzle/promises#synchronous-wait

    根据Guzzle FAQ,您应该在 RESTful 调用中使用 requestAsync()

    Guzzle 可以发送异步请求吗?

    是的。您可以使用 requestAsync、sendAsync、getAsync、headAsync、 客户端的 putAsync、postAsync、deleteAsync 和 patchAsync 方法 发送异步请求。客户端将返回一个 GuzzleHttp\Promise\PromiseInterface 对象。你可以链接然后 履行承诺。

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

    您可以使用 wait() 强制完成异步响应 返回的 promise 的方法。

    $promise = $client->requestAsync('GET', 'http://httpbin.org/get');
    $response = $promise->wait();
    

    【讨论】:

    • 主要问题是http调用是在兔子洞中进行各种函数调用。理想情况下,我希望能够将我的doHttpCallAndReturn(); 包装在一个承诺中,并在准备好时获取响应。
    • 这不是只是重述了问题而没有回答吗?我认为最初的问题是询问如何等待多个承诺,所有承诺都是异步触发的,因此预计可能以任何顺序完成。
    • @Rich 你明白了吗?
    • 使用 .then() 不会同时执行两个调用,而是一个接一个。在实践中,您通常希望发送多个请求,等待所有请求都完成,然后执行一些操作。
    【解决方案2】:

    这个问题有点老了,但我没有看到答案,所以我会试一试,也许有人会觉得它有帮助。

    您可以使用函数all($promises)

    我找不到有关此函数的文档,但您可以找到它的实现 here

    这个函数上面的注释是这样开始的:

    给定一个promise数组,返回一个promise,该promise在数组中的所有项目都被履行时被履行。

    听起来像你要找的东西,所以你可以这样做:

    $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);
    

    如果这个函数不能帮助您解决问题,那么该文件中还有其他有趣的函数,例如 some($count, $promises)any($promises)settle($promises)

    【讨论】:

    • Sleep 仍然会使程序暂停 5 秒。
    • 它似乎有效,问题是为什么。我猜它会在错误消息后终止:Call to a member function resolve() on null
    • 不,给定的代码示例不起作用:“致命错误:未捕获的错误:在 null 上调用成员函数 resolve()”
    【解决方案3】:

    你可以使用函数Utils::all($promises)->wait();

    这里是“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();
    

    如果您想获得并发工作流,重要的是您的承诺操作必须是非阻塞的。例如, sleep(1) 是阻塞操作。所以,10 个带有 sleep(1) 的 promise - 无论如何都要等待 10 秒。

    【讨论】:

      猜你喜欢
      • 2021-06-21
      • 1970-01-01
      • 2016-10-05
      • 1970-01-01
      • 1970-01-01
      • 2018-07-01
      • 2017-06-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多