асинхронный — асинхронный http-вызов с переполнением стека

У меня есть ситуация, когда у меня есть цикл, который собирается читать порцию данных из файла, отправлять эти порции в остальные API и продолжать до EOF, но я хочу, чтобы это было асинхронно внутри цикла, поэтому я не нужно ждать, пока API ответит, чтобы прочитать следующий фрагмент.
Я смотрю на Amphp и ReactPHP, потому что не могу найти решение этой проблемы, или, возможно, я не понимаю, как эти библиотеки должны использоваться.
вот псевдо того, что я делаю.

<?php

while($file.read()){

$chunk = getNextChunk();

sendChunkAsync($chunk);

}

function getNextChunk(){

echo "reading next chunk";

// read next chunk of data

}

образец с amphp

function sendChunkAsync($chunk){

Loop::run(function () {

$uri =  "https://testapi.com/api";

$client = new DefaultClient;

try {

$promises = $client->request($uri);


$responses = yield $promises;

echo "chunk processed";

} catch (Amp\Artax\HttpException $error) {

// log error

// $error->getMessage() . PHP_EOL;
}
});

}

В этом случае я бы ожидал (если чтение чанка быстрее, чем получение ответа от API) что-то вроде этого, не принимайте эту литературу, я пытаюсь проиллюстрировать это для вас.

Чтение следующего куска

Чтение следующего куска

кусок обработан

Чтение следующего куска

кусок обработан

кусок обработан

2

Решение

Я собираюсь использовать React, поскольку знаю библиотеку лучше, но они работают аналогичным образом.

РЕДАКТИРОВАТЬ: обновлено, смотрите комментарии

Это будет читать в файле и каждый раз, когда он получает кусок данных, он будет создавать вызов API и отправлять данные

<?php

require_once __DIR__ . '/vendor/autoload.php';

function async_send($config, $file, callable $proccessor)
{

$config['ssl'] = true === $config['ssl'] ? 's' : '';
$client = new \GuzzleHttp\Client([
'base_uri' => 'http' . $config['ssl'] . '://' . $config['domain'] . '/rest/all/V1/',
'verify' => false,
'http_errors' => false
]);
$loop = \React\EventLoop\Factory::create();
$filesystem = \React\Filesystem\Filesystem::create($loop);
$filesystem->getContents($file)->then(function($contents) use ($config, $proccessor, $client) {
$contents = $proccessor($contents);
$client->post($config['uri'], ['body' => $contents]);
});
}

$config = [
'domain' => 'example.com',
'ssl' => true
];
//somewhere later
$configp['uri'] = 'products';
async_send($configp, __DIR__ . 'my.csv', function ($contents) {
return json_encode($contents);
});
1

Другие решения

В случае, если кто-то еще пытается решить аналогичную проблему

<?php

require_once __DIR__ . '/../vendor/autoload.php';

use React\HttpClient\Client as ReactClient;

function async_send($loop, $filePath, callable $proccessor)
{
echo "starting";
echo "\n\r";

try {

$filesystem = \React\Filesystem\Filesystem::create($loop);

$file = $filesystem->file($filePath);
$file->open('r')
->then(function ($stream) use ($loop, $proccessor){
$stream->on('data', function ($chunk) use ($loop, $proccessor) {
$proccessor($chunk);
});
});

} catch (\Exception $e) {
echo "failed";
echo "\n\r";
}
echo "ending reading";
echo "\n\r";
}

function callApiReal($loop, $fileChunk = null)
{
echo "ready to call api". PHP_EOL;

$uri = "https://testapi.com/";
try {
$client = new ReactClient($loop);
} catch (\Exception $e) {
echo "Error";
}
echo "ready to call api";

$request = $client->request('POST', $uri, $fileChunk);

$request->on('response', function ($response) use ($uri) {

$response->on('data', function ($data_chunk) {
echo 'data chunk from api received';
echo "\n\r";
});

// subscribe to listen to the end of the response
$response->on('end', function () use ($uri) {
echo "operation has completed";
echo "\n\r";
});
});

$request->on('error', function ($error) {
// something went bad in the request
echo "Damm!";
echo "\n\r";
});

$request->end();

}

// main loop
$loop = React\EventLoop\Factory::create();

//somewhere later
async_send($loop, __DIR__ . '/my.csv', function ($chunk) use ($loop) {
echo "calling api";
callApiReal($loop, $chunk);
echo "\n\r";
});

$loop->run();
1