RxPHP / RxHttp

Http Client for RxPHP

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Duplicate $loop

ameoba32 opened this issue · comments

commented

Lets create simple GET program following rxPHP usage example on their page. It won't run as expected, because of two loops are created. First for rxPHP and second one for rxHTTP though voryx.

I have to add " \EventLoop\setLoop($loop);" line for example to work properly, otherwise subscribe is called only on script shutdown function, which is not what i want.

Can it be made more obvious?

        $loop = Factory::create();
        //You only need to set the default scheduler once
        Scheduler::setDefaultFactory(function() use($loop){
            return new Scheduler\EventLoopScheduler($loop);
        });

        // Without this line, rxHTTP has its own loop
        \EventLoop\setLoop($loop);

        $source = \Rx\React\Http::get('https://www.example.com/');

        $source->subscribe(
            function ($data) {
                echo $data, PHP_EOL;
            },
            function (\Exception $e) {
                echo $e->getMessage(), PHP_EOL;
            },
            function () {
                echo "completed", PHP_EOL;
            }
        );
        $loop->run();

I'll update the examples to make that a little clearer.

If you want to manually start the loop you can do it like:

$source = \Rx\React\Http::get('https://www.example.com/');

$source->subscribe(
    function ($data) {
        echo $data, PHP_EOL;
    },
    function (\Exception $e) {
        echo $e->getMessage(), PHP_EOL;
    },
    function () {
        echo "completed", PHP_EOL;
    }
);

\EventLoop\getLoop()->run();

That way you can take advantage of the the EventLoopScheduler being bootstrapped for you.

commented

"\EventLoop\getLoop()->run();" does not help in my example. It still creates second $loop instance and React and Rx are out of sync.

Here is what I am trying to achieve. Code below works only as is, any modifications with $loop and it stops working. Do you know why?

<?php

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

use Rx\Observable;
use React\EventLoop\Factory;
use Rx\Scheduler;

$loop = Factory::create();
Scheduler::setDefaultFactory(function() use($loop){
    return new Scheduler\EventLoopScheduler($loop);
});

\EventLoop\setLoop($loop);

$season = \Rx\Observable::fromArray(
    [
        'https://www.example.com/',
        'https://www.example.com/',
        'https://www.example.com/',
    ]
)->flatMap(
    function ($url) {
        return \Rx\React\Http::get($url);
    }
)->map(
    function ($item) {
        return strtoupper(substr(strip_tags($item), 0, 100));
    }
)->subscribe(
    function ($data) {
        echo $data, PHP_EOL;
    },
    function (\Exception $e) {
        echo $e->getMessage(), PHP_EOL;
    },
    function () {
        echo "completed", PHP_EOL;
    }
);

$loop->run();

I'm not sure what you mean by "It still creates second $loop instance and React and Rx are out of sync."

You have two options:

  1. Create a loop and bootstrap stuff yourself and pass that loop to everything, using getLoop to set the loop for RxHttp, which is what you are doing in your example.
  2. Use \EventLoop\getLoop() and pass that loop around to your react libs.

Since option 2 bootstraps the Rx async scheduler, it's a lot cleaner:

$loop = \EventLoop\getLoop();

// Use this loop on other ReactPHP libs

$season = \Rx\Observable::fromArray([
    'https://www.example.com/',
    'https://www.example.com/',
    'https://www.example.com/',
])
    ->flatMap(function ($url) {
        return \Rx\React\Http::get($url);
    })
    ->map(function ($item) {
        return strtoupper(substr(strip_tags($item), 0, 100));
    })
    ->subscribe(
        function ($data) {
            echo $data, PHP_EOL;
        },
        function (\Exception $e) {
            echo $e->getMessage(), PHP_EOL;
        },
        function () {
            echo "completed", PHP_EOL;
        }
    );

$loop->run();

The latest version was refactored to better accommodate this scenario.

https://github.com/RxPHP/RxHttp/blob/master/examples/http/advanced_get.php

commented

Thank you David! Your example above works well!

$loop is created only once and same instance is used by RxPHP because in file
vendor/voryx/event-loop/src/bootstrap.php it sets it to same instance

    \Rx\Scheduler::setDefaultFactory(function () {
        return new \Rx\Scheduler\EventLoopScheduler(\EventLoop\getLoop());
    });

What is a bit deceiving is example on this page https://github.com/ReactiveX/RxPHP where it is suggested to use next code:

$loop = Factory::create();
//You only need to set the default scheduler once
Scheduler::setDefaultFactory(function() use($loop){
    return new Scheduler\EventLoopScheduler($loop);
});

if I use it with RxHTTP as is, then two different instances of $loop are created. Now I understand much better how loops are created and used. Thank you for clarification!

Awesome technology in PHP! If you remember I was really impressed from your speech at SunShinePHP2016. So I am putting some pieces in production!

@ameoba32 Internally we use a static loop to simplify loop creation and compatibility with our other libs, which does make it a little confusing if you're used to passing the loop around, like in other ReactPHP libs.
I'm considering using a static constructor for lib instances that use the static loop, so we can maintain consistency with the other ReactPHP libs, without giving up the convenience of the static loop. ie: https://github.com/RxPHP/RxHttp/blob/static_constructor/src/Client.php#L21-L24

I was at SunShinePHP2016, but it was @luijar that gave the talk 😄