I'm using Symfony 3 and ReactPHP library for control all my functionality and I need to execute multiple calls to same function (subFunction()
in code) asynchronously.
I have 2 projects (project1 and project2):
Project 1 code:
/**
* Loop an array of urls and call sub function.
**/
public function startFunction() {
$finalResponse = [];
$urls = ['www.google.es', 'www.github.com', 'www.bitbucket.org'];
foreach ($urls as $url) {
$res = $this->subFunction($url); // subfunction call ( **IT MAY TAKE A LONG TIME !!** )
$finalResponse[] = $res;
}
return $finalResponse;
}
/**
* Uses Factory loop to get the Promise returned by finalRequest function.
**/
private function subFunction($url) {
$loop = \React\EventLoop\Factory::create();
$classA = new Project2\ClassA();
$finalResponse = null;
// project 2 function call
$classA->finalRequest($url)->then(function($response) use(
&$finalResponse
) {
$finalResponse = $response;
})
return $finalResponse;
}
Project 2 code:
classA {
/**
* Makes an React\HttpClient request (GET) to sent url and return his value inside a Promise.
**/
public function finalRequest($url) {
$generalDeferred = new Deferred();
$generalPromise = $generalDeferred->promise();
// make React\HttpClient request
$request = $client->request('GET', $url);
$request->on('response', function ($response) use($generalDeferred) {
$response->on('data', function ($response) {
$generalDeferred->resolve($response);
});
});
$request->end();
return $generalPromise;
}
}
Problem is that on every subFunction($url)
call, the program stops until the sub Function gets the response, but I need to do this asynchronously because this subFunction could take many seconds.
So I would like to launch all subFunction($url)
calls at the same time, and get all responses asynchronously.
It's possible solve this problem? Thanks.
First of all you can only have one loop running in an app. Second you have to make the loop run: https://reactphp.org/event-loop/
You should create the app and then register all the services and events, start the loop and leave it running as a server.
$loop = React\EventLoop\Factory::create();
$server = stream_socket_server('tcp://127.0.0.1:8080');
stream_set_blocking($server, 0);
$loop->addReadStream($server, function ($server) use ($loop) {
[...]
});
$loop->addPeriodicTimer(5, function () {
[...]
});
$loop->run(); <---- you will not execute anything behind this point.
Why? https://github.com/reactphp/event-loop/blob/master/src/ExtLibeventLoop.php#L196
public function run()
{
$this->running = true;
while ($this->running) { <------------------------------
$this->futureTickQueue->tick();
$flags = EVLOOP_ONCE;
if (!$this->running || !$this->futureTickQueue->isEmpty()) {
$flags |= EVLOOP_NONBLOCK;
} elseif (!$this->streamEvents && !$this->timerEvents->count()) {
break;
}
event_base_loop($this->eventBase, $flags);
}
}
For the use you do of the loop I would recommend to use Guzzle Async: http://docs.guzzlephp.org/en/stable/faq.html
$finalResponse = [];
$promises = [];
$urls = ['www.google.es', 'www.github.com', 'www.bitbucket.org'];
foreach ($urls as $index => $url) {
$promise = $client->requestAsync('GET', $url);
$promise->then(function ($response) use ($index, &$finalResponse) {
$finalResponse[$index]=$response;
});
$promises[$index]=$promise;
}
foreach ($promises as $promise) {
$promise->wait();
}
return $finalResponse;