Не удается получить обещания ReactPHP для асинхронного выполнения

У меня есть PHP-скрипт, который обрабатывает данные, загруженные из нескольких REST API, в стандартизированный формат и создает массив или таблицу этих данных. В настоящее время скрипт выполняет все синхронно и поэтому занимает слишком много времени.

Я пытался узнать, как выполнить функцию, которая извлекает и обрабатывает данные, одновременно или асинхронно, чтобы общее время было временем самого медленного вызова. Из моего исследования выясняется, что ReactPHP или Amp являются правильными инструментами.

Тем не менее, мне не удалось создать тестовый код, который на самом деле выполняется правильно. Прилагается простой пример, где mysquare() представляет мою более сложную функцию. Из-за отсутствия в сети примеров именно того, чего я пытаюсь достичь, я был вынужден использовать метод грубой силы с 3 примерами, перечисленными в моем коде.

Q1: я использую правильный инструмент для работы?

Q2: Можете ли вы исправить мой пример кода для асинхронного выполнения?

NB: Я настоящий новичок, поэтому был бы признателен за самый простой пример кода с минимумом языка программирования высокого уровня.

<?php
require_once("../vendor/autoload.php");

for ($i = 0; $i <= 4; $i++) {

    // Experiment 1
    $deferred[$i] = new React\Promise\Deferred(function () use ($i) {
        echo $x."\n";
        usleep(rand(0, 3000000));  // Simulates long network call
        return array($x=> $x * $x);
    });

    // Experiment 2
    $promise[$i]=$deferred[$i]->promise(function () use ($i) {
        echo $x."\n";
        usleep(rand(0, 3000000));  // Simulates long network call
        return array($x=> $x * $x);
    });

    // Experiment 3
    $functioncall[$i] = function () use ($i) {
        echo $x."\n";
        usleep(rand(0, 3000000));  // Simulates long network call
        return array($x=> $x * $x);
    };
}

$promises = React\Promise\all($deferred); // Doesn't work
$promises = React\Promise\all($promise); // Doesn't work
$promises = React\Promise\all($functioncall); // Doesn't work

// print_r($promises);  // Doesn't return array of results but a complex object

//  This is what I would like to execute simulatenously with a variety of inputs
function mysquare($x)
{
    echo $x."\n";
    usleep(rand(0, 3000000));  // Simulates long network call
    return array($x=> $x * $x);
}

2 ответа

Асинхронный не означает, что несколько потоков выполняются параллельно. 2 функции могут действительно выполняться только в одно и то же время, если они (например) выполняют IO, например, HTTP-запрос.

блоки usleep(), так что вы ничего не получите. И ReactPHP, и Amp сами будут иметь какую-то функцию "сна", встроенную прямо в цикл обработки событий.

По той же причине вы не сможете просто использовать curl, потому что он также будет блокироваться из коробки. Вам нужно использовать HTTP-библиотеки, которые React и Amp предоставляют и / рекомендуют.

Поскольку ваша конечная цель - просто выполнять HTTP-запросы, вы также не можете использовать ни одну из этих платформ и просто использовать функции curl_multi. Они немного сложны в использовании, хотя.

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

TL; DR

Я перешел с ReactPHP, потому что я не понял, чтобы использовать amphp/parallel-functions который предлагает упрощенный интерфейс конечного пользователя... пример кода с использованием этого интерфейса прилагается.

<?php
require_once("../vendor/autoload.php");

use function Amp\ParallelFunctions\parallelMap;
use function Amp\Promise\wait;

$start = \microtime(true);

$mysquare = function ($x) {
    sleep($x);  // Simulates long network call
    //echo $x."\n";
    return $x * $x;
};

print_r(wait(parallelMap([5,4,3,2,1,6,7,8,9,10], $mysquare)));

print 'Took ' . (\microtime(true) - $start) . ' milliseconds.' . \PHP_EOL;

Пример кода выполняется за 10,2 секунды, что немного дольше, чем самый длительный экземпляр $mysquare().

В моем реальном случае использования я смог получить данные через HTTP из 90 отдельных источников примерно за 5 секунд.

Заметки:

amphp/parallel-functions Библиотека, кажется, использует темы под капотом. Исходя из моего предварительного опыта, кажется, что для этого требуется гораздо больше памяти, чем для однопоточного скрипта PHP, но я еще не до конца понял. Это было подчеркнуто, когда я передавал большой массив в $ mysquare через выражение "use ($myarray)", и размер массива составлял 65 МБ. Это привело к тому, что код зашел в тупик и экспоненциально увеличил время выполнения, что потребовало на несколько порядков дольше, чем синхронное выполнение. Кроме того, использование памяти достигло 5G! В какой-то момент я понял, что amphp дублирует $ myarray для каждого экземпляра. Переработка моего кода, чтобы избежать выражения "use ($myarray)", устранила эту проблему.

Другие вопросы по тегам