gzhegow
@gzhegow
aka "ОбнимиБизнесмена"

Как реализовать асинхронность с ReactPHP в Windows?

Если я правильно понял идею - подымается процесс сервера, который умеет выполнять код асинхронно

У меня запускается sleep(3), sleep(2), sleep(1) асинхронно
Я должен увидеть вывод 1-2-3, и ожидание 3 секунды т.к. браузер ожидает ответ

Вместо этого ожидание 6 секунд и 3-2-1, как будто все работает синхронно

Вот код, который не работает, может подскажете что я делаю не так
https://drive.google.com/file/d/1SCwzziaulDQSYgDm6...

Это из-за Windows?
Просто если эта штука работает только в Linux, то в там есть pthreads.so который отлично работает и смысл с этой обертки?

К слову в CLI в Windows pthreads.dll тоже работает (но только в консоли) - я запускал скрипт с этим подключенным модулем и все равно 6 секунд.
  • Вопрос задан
  • 153 просмотра
Решения вопроса 1
@Flying
Нет, это не из-за Windows, проблема в вашем коде, хотя она и не так очевидна как кажется, пришлось повозиться чтобы найти ответ.

Если вкратце то основная проблема - в использовании sleep() для эмуляции задержки. Ведь в отличие от того же setTimeout() в JavaScript который нативно использует event loop, а следовательно асинхронен, sleep() в PHP - это просто задержка т.е. блокирующая операция. Ваш код не мог продолжаться дальше пока не отработает sleep(), отсюда и последовательность выполнения которая по факту практически синхронная.

Для получения асинхронной задержки вам необходимо было использовать LoopInterface::addTimer(), тогда код начинает работать как надо.

Немного изменённый вариант вашего кода, с форматированием и выводом задержки давал:
[3] 3 sec
[2] 5 sec
[1] 6 sec

причём и на Windows и на Linux.

Если же использовать вариант приведённый ниже - то результат становится ожидаемым, и на Windows и на Linux:
[1] 1 sec
[2] 2 sec
[3] 3 sec


Ниже приведён код, дающий правильный результат, я не стал сильно менять структуру, хотя её можно упростить.

server.php
<?php

use React\EventLoop\Factory;
use React\Http\Response;
use React\Http\Server;

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

$loop = Factory::create();
$socket = new \React\Socket\Server($argv[1] ?? '0.0.0.0:0', $loop);

$server = new Server(static function () use ($loop) {
    $test = new Async($loop);
    return $test->run()->then(static function () use ($test) {
        return new Response(
            200,
            ['Content-Type' => 'text/plain'],
            (string)$test
        );
    });
});
$server->listen($socket);

echo 'Listening on ' . str_replace('tcp:', 'http:', $socket->getAddress()) . PHP_EOL;
$loop->run();

async.php
<?php

use React\EventLoop\LoopInterface;
use React\Promise\PromiseInterface;

class Async
{
    protected $_stdout;
    /**
     * @var LoopInterface
     */
    private $loop;

    /**
     * @param LoopInterface $loop
     */
    public function __construct(LoopInterface $loop)
    {
        $this->loop = $loop;
    }

    public function __toString()
    {
        return $this->_stdout;
    }

    public function run(): PromiseInterface
    {
        $start = time();
        $handler = function ($x) use ($start) {
            $this->_stdout .= sprintf("[%d] %d sec\n", $x, time() - $start);
        };
        return React\Promise\all([
            $this->asyncAction(3)->then($handler),
            $this->asyncAction(2)->then($handler),
            $this->asyncAction(1)->then($handler),
        ]);
    }

    protected function asyncAction(int $sleep = 0): PromiseInterface
    {
        return new React\Promise\Promise(function ($resolve, $reject) use ($sleep) {
            $this->action($sleep, static function ($e, $result) use ($resolve, $reject) {
                if ($e) {
                    $reject($e);
                } else {
                    $resolve($result);
                }
            });
        });
    }

    protected function action(int $sleep, callable $callback): void
    {
        $this->loop->addTimer($sleep, static function () use ($sleep, $callback) {
            $callback(null, $sleep);
        });
    }
}
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы
19 апр. 2024, в 18:38
1500 руб./в час
19 апр. 2024, в 18:36
1500 руб./за проект
19 апр. 2024, в 18:36
30000 руб./за проект