LoginSignup
9
8

More than 5 years have passed since last update.

ReactPHP React/PromiseでRetry処理

Posted at

単純に書いたら、sleep して再度実行とかやるかもしれないけど、イベントループではそんなことしたら全部止まっちゃうので、3秒後に実行するのにタイマー使って実装してみた。

実装Aと実装Bはやっていることは変わらないんだけど、実装Aは素直にリトライ処理を実装した感じで、実装Bはresolverとして利用することを前提としてクロージャでラップして返しているだけ、実装Aの場合、一度自分でラップしなきゃならないのが気になって、実装Bを作ってみたけど...。どっちが良いだろう...。

実装A

/**
 * @param LoopInterface $loop
 * @param callable      $callback  リトライする処理
 * @param int           $interval  リトライ間隔
 * @param int           $maximum   最大リトライ回数
 * @param int           $retry     リトライカウンタ
 * @param Deferred      $deferred
 *
 * @return PromiseInterface
 */
function retry($loop, $callback, $interval = 3, $maximum = 3, $retry = 0, $deferred = null)
{
    $deferred = $deferred ?: new \React\Promise\Deferred();

    /** @var PromiseInterface $promise */
    $promise = $callback();

    $promise->then(
        function ($response) use ($deferred) {
            $deferred->resolve($response);
        },
        function (\Exception $e = null)
            use ($loop, $callback, $interval, $maximum, $retry, $deferred) {

            if ($e !== null)
                echo sprintf('%s:%s in %s at %d.', get_class($e), $e->getMessage(), __FILE__, __LINE__).PHP_EOL;

            if ($retry++ >= $maximum)
                return $deferred->reject($e);

            // タイマーで $interval 後にリトライする
            $loop->addTimer($interval,
                function ($timer)
                    use ($loop, $callback, $interval, $maximum, $retry, $deferred) {

                    retry($loop, $callback, $interval, $maximum, $retry, $deferred);
                }
            );
        }
    );

    return $deferred->promise();
}

実装Aサンプル

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

$client = new \GuzzleHttp\Client([
    'handler' => new \WyriHaximus\React\RingPHP\HttpClientAdapter($loop)
]);

$deferred = new React\Promise\Deferred();

$deferred->promise()
    ->then(function ($url) use ($client, $loop) {
        // リトライする
        return retry($loop, function () use ($url, $client, $loop) {
            return $client->get($url, ['future' => true])->promise();
        });
    })
    ->then(function ($response) {
        echo $response->getStatusCode();
    });
$deferred->resolve('http://127.0.0.1/');

$loop->run();

実装B

/**
 * @param LoopInterface $loop
 * @param callable      $callback
 * @param int           $interval
 * @param int           $maximum
 * @param int           $retry
 * @param Deferred      $deferred
 *
 * @return callable
 */
function retry_wrapper($loop, $callback, $interval = 3, $maximum = 3, $retry = 0, $deferred = null)
{
    return function ($response)
        use ($loop, $callback, $interval, $maximum, $retry, $deferred) {

        $deferred = $deferred ?: new \React\Promise\Deferred();

        $promise = $callback($response);

        $promise->then(
            function ($response) use ($deferred) {
                $deferred->resolve($response);
            },
            function (\Exception $e = null)
                use ($loop, $callback, $response, $interval, $maximum, $retry, $deferred) {

                if ($e !== null)
                    echo sprintf('%s:%s in %s at %d.', get_class($e), $e->getMessage(), __FILE__, __LINE__).PHP_EOL;

                if ($retry++ >= $maximum)
                    return $deferred->reject($e);

                $loop->addTimer($interval,
                    function ($timer)
                        use ($loop, $callback, $response, $interval, $maximum, $retry, $deferred) {

                        $wrapped = retry_wrapper(
                            $loop, $callback, $interval, $maximum, $retry, $deferred
                        );
                        $wrapped($response);
                    }
                );
            }
        );

        return $deferred->promise();
    };
}

実装Bサンプル

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

$client = new \GuzzleHttp\Client([
    'handler' => new \WyriHaximus\React\RingPHP\HttpClientAdapter($loop)
]);

$deferred = new React\Promise\Deferred();

$deferred->promise()
    ->then(retry_wrapper($loop, function ($url) use ($client, $loop) {

        return $client->get($url, ['future' => true])->promise();

    }))->then(function ($response) {
        echo $response->getStatusCode();
    });
$deferred->resolve('http://127.0.0.1/');

$loop->run();

composer.json

{
    "require": {
        "react/react": "0.4.*",
        "react/promise": "~2.1",
        "guzzlehttp/guzzle": "~5.1",
        "wyrihaximus/react-guzzle-ring": "dev-master"
    }
}
9
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
8