0
votes

I have a controller like so:

<?php

namespace App\Http\Controllers;

use App\Helpers\LogHelper;


class ExampleController {
    public function test() {
        $client = new \GuzzleHttp\Client();

        LogHelper::logTo('s3_logs', 'starting requests');
        $p1 = self::testRequest1($client);
        $p2 = self::testRequest2($client);
        $p3 = self::testRequest3($client);

        return response()->json(['success' => true]);
    }

    private static function testRequest1($client) {
        return $client->requestAsync('GET', 'http://www.zara.com/')
            ->then(function($response) {
                LogHelper::logTo('s3_logs', 'resolved from zara.com');
            }, function($e) {
                LogHelper::logTo('s3_logs', 'rejected from zara.com');
            });
    }
    private static function testRequest2($client) {
        return $client->requestAsync('GET', 'http://www.google.com')
            ->then(function($response) {
                LogHelper::logTo('s3_logs', 'resolved from google.com');
            }, function($e) {
                LogHelper::logTo('s3_logs', 'rejected from google.com');
            });
    }
    private static function testRequest3($client) {
        return $client->requestAsync('GET', 'http://httpbin.org/get')
            ->then(function($response) {
                LogHelper::logTo('s3_logs', 'resolved from httpbin.org');
            }, function($e) {
                LogHelper::logTo('s3_logs', 'rejected from httpbin.org');
            });
    }
}

The 'test' method is running just fine and returning the 'success' json exactly correctly, I've triple checked.

But those async requests are not running at all, as far as I can tell. My LogHelper::logTo function (which again runs fine, I'm certain) only logs 'starting requests', not any of the other things.

I tried this as an alternative -- waiting for the promises to resolve, but I don't think laravel likes this, it's erroring when I do it this way:

public function test() {
    $client = new \GuzzleHttp\Client();

    LogHelper::logTo('s3_logs', 'starting requests');
    $p1 = self::testRequest1($client);
    $p2 = self::testRequest2($client);
    $p3 = self::testRequest3($client);

    return $p1->then(function() {
        return response()->json(['success' => true]);
    });
}

This errors with 'The Response content must be a string or object implementing __toString()'

How can I return my response AFTER a Guzzle promise resolves? And even regardless of returning a response separately, why aren't my async requests running and resolving?

1
Your second approach is close to the right one. This answer contains the code you should be using instead.PtrTon
@PtrTon if you want to get some SO points for your comment, copy whatever you want out of what you linked and the answer I posted below. I'll choose yours as correct.TKoL
No worries, just accept your own answer. Glad you found the solution to your issue.PtrTon

1 Answers

1
votes

Thanks to @PtrTron I found the right answer:

First this worked:

    $p1->wait();
    $p2->wait();
    $p3->wait();
    return response()->json(['success' => true]);

Not only did it work, I verified that the requests resolved out of order as well, meaning it's definitely properly asynchronous.

And then I made this work, which is slightly better:

    $results = \GuzzleHttp\Promise\all([$p1, $p2, $p3])->wait();
    return response()->json(['success' => true]);

You can alternatively use

    $results = \GuzzleHttp\Promise\settle([$p1, $p2, $p3])->wait();

If you don't catch your promise rejections, errors from all can be caught by wrapping in a try\catch, while errors from settle can be seen in the $results array, where each member of that array is an associative array with a property state which can be 'rejected' or 'fulfilled'