とまれーっ うごけーっ

深夜にTwitterでGuzzle(PHPのHTTPクライアントライブラリ)が[Promises/A+(日本語訳)](https://triple-underscore.github.io/Promises_Aplus-ja.html)のインターフェイスを持ってるって話が出てたので、深夜のテンションでサンプルコードを書いたらこうなった。

https://twitter.com/tadsan/status/829780582666104833

あと、[わーい、すごーい](http://qiita.com/t_ishida/items/bae7e62950d81f427f13)の便乗。

http://qiita.com/t_ishida/items/bae7e62950d81f427f13

```php
<?php

include_once getenv('HOME') . '/.composer/vendor/autoload.php';
use GuzzleHttp\Client;

$client = new Client(['allow_redirects' => ['on_redirect' => function () {
    throw new \Exception();
}]]);

$kaban  = say("かばん");
$sarval = say("サーバル");
$sarval("とまれーっ");

//$bus = $client->requestAsync('POST', 'http://kemono-friends.jp/library'
$bus = $client->requestAsync('POST', 'http://localhost/library'
)->then(function () use ($sarval) { $sarval("たーのしー"); },
        function () use ($sarval) { $sarval("ｳﾞｯ "); sleep(1); }
)->then(function () use ($kaban)  { $kaban("あぶないよー"); });

sleep(3);

$sarval("うごけーっ");
$bus->wait();

exit;

function say($name) {
    return function ($script) use ($name) {
        printf("%s「%s」\t[%s]\n", $name, $script, date('H:i:s'));
    };
}
```

PHPで非同期処理ができるのか、といったら実はちょっと厳しい。上記の処理では`$bus->wait()`が呼び出されるまで、待てど暮せど何も起こらない。

もちろんGuzzleの`requestAsync()`はお遊びで実装されてるわけではなくて、実用的な利用方法は @suzuki さんの[Guzzle Promiseを使った 非同期処理によるAPIコールの高速化 // Speaker Deck](https://speakerdeck.com/suzuki/guzzle-promisewoshi-tuta-fei-tong-qi-chu-li-niyoruapikorufalsegao-su-hua)に詳しい。

しかし「非同期処理」と呼ぶと、おそらく間違ってはないものの若干の期待を抱かせすぎなところはあって、「複数のHTTPリクエストを効率的に処理することができる」のような理解をしておくのが妥当だろう。

以下のようなコードがどのように実行されるかを見ると、どのような挙動をするのか理解できるかもしれない。

```php
<?php

include_once getenv('HOME') . '/.composer/vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Pool;
use GuzzleHttp\Psr7\Request;

$kaban  = say("かばん");
$sarval = say("サーバル");
$sarval("とまれーっ");

$resuests = function () use ($sarval) {
    while (true) {
        $sarval("うごけーっ");
        yield new Request('GET', 'http://localhost/library');
    }
};

$client = new Client(['allow_redirects' => ['on_redirect' => function () {
    throw new \Exception();
}]]);
$pool = new Pool($client, take($resuests(), 7), [
    'concurrency' => 3,
    'rejected' => function () use ($sarval, $kaban) {
        $sarval("ｳﾞｯ ");
        sleep(1);
        $kaban("あぶないよー");
    }
]);
$promise = $pool->promise();
$promise->wait();

$kaban("あははははは");

exit;

function say($name) {
    return function ($script) use ($name) {
        printf("%s「%s」\t[%s]\n", $name, $script, date('H:i:s'));
    };
}

function take($iter, $n) {
    $i = 0;
    foreach ($iter as $k => $x) {
        if ($n <= $i++) { break; }
        yield $k => $x;
    }
}
```

サーバルちゃんは3並列で合計7回バスにぶつけられる運命にある。 (自分で書いておいて意味がわからない)

```
サーバル「とまれーっ」	[09:40:11]
サーバル「うごけーっ」	[09:40:11]
サーバル「うごけーっ」	[09:40:11]
サーバル「うごけーっ」	[09:40:11]
サーバル「ｳﾞｯ 」	[09:40:11]
かばん「あぶないよー」	[09:40:12]
サーバル「うごけーっ」	[09:40:12]
サーバル「ｳﾞｯ 」	[09:40:12]
かばん「あぶないよー」	[09:40:13]
サーバル「うごけーっ」	[09:40:13]
サーバル「ｳﾞｯ 」	[09:40:13]
かばん「あぶないよー」	[09:40:14]
サーバル「うごけーっ」	[09:40:14]
サーバル「ｳﾞｯ 」	[09:40:14]
かばん「あぶないよー」	[09:40:15]
サーバル「うごけーっ」	[09:40:15]
サーバル「ｳﾞｯ 」	[09:40:15]
かばん「あぶないよー」	[09:40:16]
サーバル「うごけーっ」	[09:40:16]
サーバル「ｳﾞｯ 」	[09:40:16]
かばん「あぶないよー」	[09:40:17]
サーバル「ｳﾞｯ 」	[09:40:17]
かばん「あぶないよー」	[09:40:18]
かばん「あははははは」	[09:40:18]
```

最初の`サーバル「うごけーっ」   [09:40:11]`が三回呼ばれてるのが比較的良い感じっぽく見えるが、そのあと`サーバル「ｳﾞｯ 」` と `かばん「あぶないよー」`は*必ず*並び、しかも`sleep(1)`で**処理全体をブロック**してしまってることがわかる。

このことから、Guzzleの`requestAsync()`は飽くまで非同期リクエストのための機能で、**それ以外の処理は非同期にならない**ことを注意しておくと良い。

ではPHPでは非同期処理できないのかと言ったらそうでもなく、 @niisan-tokyo さんの[PHPで作る非同期処理 // Speaker Deck](https://speakerdeck.com/niisantokyo/phpdezuo-rufei-tong-qi-chu-li)に詳しいように様々な試みがあって、[ReactPHP？時代はRecoilPHPだ！](http://qiita.com/mpyw/items/f448a4c582c78ddb9365)など参照。

## 環境について

`composer global show`すると、こんな感じ。`composer global guzzlehttp/guzzle`すると入るかも。

```
guzzlehttp/guzzle     6.2.2    Guzzle is a PHP HTTP client library
guzzlehttp/promises   v1.3.1   Guzzle promises library
guzzlehttp/psr7       1.3.1    PSR-7 message implementation
```

## さいごに

深夜のテンションで書いたサンプルコードを適当に深掘りしていった結果、意味不明な記事になった。

*たーのしー*