I'm learning amphp. I want to convert sync call to async call using event loop in amphp. My sample code use file_get_contents as sample blocking call.
Using sync call, it look like this:
$uris = [
"https://google.com/",
"https://github.com/",
"https://stackoverflow.com/",
];
$results = [];
foreach ($uris as $uri) {
var_dump("fetching $uri..");
$results[$uri] = file_get_contents($uri);
var_dump("done fetching $uri.");
}
foreach ($results as $uri => $result) {
var_dump("uri : $uri");
var_dump("result : " . strlen($result));
}
And the output:
string(30) "fetching https://google.com/.."
string(34) "done fetching https://google.com/."
string(30) "fetching https://github.com/.."
string(34) "done fetching https://github.com/."
string(37) "fetching https://stackoverflow.com/.."
string(41) "done fetching https://stackoverflow.com/."
string(25) "uri : https://google.com/"
string(14) "result : 48092"
string(25) "uri : https://github.com/"
string(14) "result : 65749"
string(32) "uri : https://stackoverflow.com/"
string(15) "result : 260394"
I know there's artax that would do the call in async. But, I want to learn how to correctly turn blocking code into async concurrent code (not parallel). I'm already success implementing it in parallel using amp.
I believe the correct output if I successfully implement it in async in amp, will be something like this:
string(30) "fetching https://google.com/.."
string(30) "fetching https://github.com/.."
string(37) "fetching https://stackoverflow.com/.."
string(34) "done fetching https://google.com/."
string(34) "done fetching https://github.com/."
string(41) "done fetching https://stackoverflow.com/."
string(25) "uri : https://google.com/"
string(14) "result : 48124"
string(25) "uri : https://github.com/"
string(14) "result : 65749"
string(32) "uri : https://stackoverflow.com/"
string(15) "result : 260107"
I've tried with this code:
<?php
require __DIR__ . '/vendor/autoload.php';
use Amp\Loop;
use function Amp\call;
Loop::run(function () {
$uris = [
"https://google.com/",
"https://github.com/",
"https://stackoverflow.com/",
];
foreach ($uris as $uri) {
$promises[$uri] = call(function () use ($uri) {
var_dump("fetching $uri..");
$result = file_get_contents($uri);
var_dump("done fetching $uri.");
yield $result;
});
}
$responses = yield $promises;
foreach ($responses as $uri => $result) {
var_dump("uri : $uri");
var_dump("result : " . strlen($result));
}
});
Instead giving me my expected result, it give me this error:
string(30) "fetching https://google.com/.."
string(34) "done fetching https://google.com/."
string(30) "fetching https://github.com/.."
string(34) "done fetching https://github.com/."
string(37) "fetching https://stackoverflow.com/.."
string(41) "done fetching https://stackoverflow.com/."
PHP Fatal error: Uncaught Amp\InvalidYieldError: Unexpected yield; Expected an instance of Amp\Promise or React\Promise\PromiseInterface or an array of such instances; string yielded at key 0 on line 20
The result also seems run in sync, not async.
How should I correctly do it?
You need to use a non-blocking implementations of the functions you use.
file_get_contentsis blocking. You can find a non-blocking implementation for example inamphp/file. If you replacefile_get_contentswithAmp\File\get, it should work as expected.